在 C# 中将 int 转换为 4 个字节的最快方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8827649/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 04:40:32  来源:igfitidea点击:

Fastest way to convert int to 4 bytes in C#

c#.netbit-manipulation

提问by MichaelT

What is a fastest way to convert int to 4 bytes in C# ?

在 C# 中将 int 转换为 4 个字节的最快方法是什么?

Fastest as in execution time not development time.

最快的执行时间而不是开发时间。

My own solution is this code:

我自己的解决方案是这个代码:

byte[] bytes = new byte[4];
unchecked
{
 bytes[0] = (byte)(data >> 24);
 bytes[1] = (byte)(data >> 16);
 bytes[2] = (byte)(data >> 8);
 bytes[3] = (byte)(data);
}

Right now I see that my solution outperforms both structand BitConverterby couple of ticks.

现在我看到我的解决方案在这两个方面都表现出色,struct并且表现出色BitConverter

I think the unsafe is probably the fastest option and accept that as an answer but I would prefer to use a managed option.

我认为不安全可能是最快的选择,并接受它作为答案,但我更愿意使用托管选项。

采纳答案by Hans Passant

A byte* cast using unsafe code is by far the fastest:

使用不安全代码的 byte* 转换是迄今为止最快的:

    unsafe static void Main(string[] args) {
        int i = 0x12345678;
        byte* pi = (byte*)&i;
        byte lsb = pi[0];  
        // etc..
    }

That's what BitConverter does as well, this code avoids the cost of creating the array.

这也是 BitConverter 所做的,这段代码避免了创建数组的成本。

回答by Darin Dimitrov

What is a fastest way to convert int to 4 bytes in C# ?

在 C# 中将 int 转换为 4 个字节的最快方法是什么?

Using a BitConverterand it's GetBytesoverload that takes a 32 bit integer:

使用BitConverter和它的GetBytes重载需要一个 32 位整数:

int i = 123;
byte[] buffer = BitConverter.GetBytes(i);

回答by George Duckett

Note the BitConverter may not be the fastest as the test below shows.

请注意,BitConverter 可能不是最快的,如下面的测试所示。

Use the BitConverterclass, specifically the GetBytesmethod that takes an Int32parameter:

使用BitConverter该类,特别GetBytes是采用Int32参数的方法:

var myInt = 123;
var bytes = BitConverter.GetBytes(myInt);

You can use BitConverter.IsLittlEndianto determine the byte order based on the CPU architecture.

您可以使用BitConverter.IsLittlEndian基于 CPU 架构来确定字节顺序。



EDIT: The test below isn't conclusive due to compiler optimisations.

编辑:由于编译器优化,下面的测试不是决定性的。



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    [StructLayout(LayoutKind.Explicit)]
    struct FooUnion
    {
        [FieldOffset(0)]
        public byte byte0;
        [FieldOffset(1)]
        public byte byte1;
        [FieldOffset(2)]
        public byte byte2;
        [FieldOffset(3)]
        public byte byte3;

        [FieldOffset(0)]
        public int integer;
    }
    class Program
    {
        static void Main(string[] args)
        {
            testUnion();
            testBitConverter();

            Stopwatch Timer = new Stopwatch();

            Timer.Start();
            testUnion();
            Timer.Stop();

            Console.WriteLine(Timer.ElapsedTicks);

            Timer = new Stopwatch();

            Timer.Start();
            testBitConverter();
            Timer.Stop();

            Console.WriteLine(Timer.ElapsedTicks);
            Console.ReadKey();
        }

        static void testBitConverter()
        {
            byte[] UnionBytes;

            for (int i = 0; i < 10000; i++)
            {
                UnionBytes = BitConverter.GetBytes(i);
            }
        }

        static void testUnion()
        {
            byte[] UnionBytes;

            for (int i = 0; i < 10000; i++)
            {
                FooUnion union = new FooUnion() { integer = i };

                UnionBytes = new byte[] { union.byte0, union.byte1, union.byte2, union.byte3 };

            }
        }
    }
}

回答by TomTom

The fastest way is with a struct containing 4 bytes.

最快的方法是使用包含 4 个字节的结构。

  • In a defined layout (at byte position 0, 1, 2, 3
  • And an int32 that starts at position 0.
  • Put in the 4 variables, read out the byte.
  • Finished.
  • 在定义的布局中(在字节位置 0、1、2、3
  • 还有一个从位置 0 开始的 int32。
  • 放入4个变量,读出字节。
  • 完成的。

Significantly faster than the BitConverter.

明显快于 BitConverter。

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspx

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspx

has the necessary attribute.

有必要的属性。

[StructLayout(LayoutKind.Explicit)]
struct FooUnion
{
    [FieldOffset(0)]
    public byte byte0;
    [FieldOffset(1)]
    public byte byte1;
    [FieldOffset(2)]
    public byte byte2;
    [FieldOffset(3)]
    public byte byte3;

    [FieldOffset(0)]
    public int integer;

}

回答by Jon Hanna

class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        unsafe{
            byte[] byteArray = new byte[4];
            for(int i = 0; i != int.MaxValue; ++i)
            {
            fixed(byte* asByte = byteArray)
               *((int*)asByte) = 43;
               }
        }
        Console.WriteLine(sw.ElapsedMilliseconds);
        Console.Read();
    }
}

Averages around 2770ms on my machine while

在我的机器上平均大约 2770 毫秒,而

[StructLayout(LayoutKind.Explicit)]
struct Switcher
{
  [FieldOffset(0)]
  public int intVal;
  [FieldOffset(0)]
  public byte b0;
  [FieldOffset(1)]
  public byte b1;
  [FieldOffset(2)]
  public byte b2;
  [FieldOffset(3)]
  public byte b3;
}
class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        byte[] byteArray = new byte[4];
        Switcher swi = new Switcher();
        for(int i = 0; i != int.MaxValue; ++i)
        {
          swi.intVal = 43;
          byteArray[0] = swi.b0;
          byteArray[1] = swi.b1;
          byteArray[2] = swi.b2;
          byteArray[3] = swi.b3;
        }
        Console.WriteLine(sw.ElapsedMilliseconds);
        Console.Read();
    }
}

Averages around 4510ms.

平均约为 4510 毫秒。

回答by Ilya Chernomordik

I have done a research on the times needed to serialize a basic type to byte array. I did it for the case when you already have an array and offset where you want to put your data. I guess that's really an important case compared to theoretical get an array of 4 bytes because when you are serializing something then it's exactly what you need. I have figured out that the answer to what method is faster depends on what type you want to serialize. I have tried few methods:

我对将基本类型序列化为字节数组所需的时间进行了研究。当您已经有一个数组和要放置数据的偏移量时,我就这样做了。我想与理论上获取 4 个字节的数组相比,这确实是一个重要的案例,因为当您序列化某些内容时,这正是您所需要的。我发现哪种方法更快的答案取决于您要序列化的类型。我尝试了几种方法:

  1. Unsafe reference with an additional buffer overrun check
  2. GetBytes + consequent Buffer.BulkCopy (This is essentially the same as 1 plus overhead)
  3. Direct assignment with shift ( m_Bytes[offset] = (byte)(value >> 8)
  4. Direct assignment with shift and bitwise & m_Bytes[offset] = (byte)((i >> 8) & 0xFF)
  1. 带有额外缓冲区溢出检查的不安全引用
  2. GetBytes + 随之而来的 Buffer.BulkCopy(这个和 1 加开销基本一样)
  3. 使用 shift 直接赋值 ( m_Bytes[offset] = (byte)(value >> 8)
  4. 使用移位和按位 & 直接赋值 m_Bytes[offset] = (byte)((i >> 8) & 0xFF)

I ran all of the test 10 mln times. Below are the results in milliseconds

我运行了所有测试 1000 万次。以下是以毫秒为单位的结果

      Long   Int   Short  Byte   Float   Double
1     29     32     31     30     29      34
2     209    233    220    212    208     228
3     63     24     13     8      24      44
4     72     29     14          

As you can see the unsafe way is much faster for long and double (unsigned versions are about the same as their signed versions so they are not in the table). For short/int/float the fastest way is the 2/4/4 assignments with shift. For byte the fastest is obviously the simple assignment. So regarding the original question - the assignment way is the best. This is the example of such a function in a fastest way:

正如您所看到的,long 和 double 的不安全方式要快得多(未签名版本与其签名版本大致相同,因此它们不在表中)。对于 short/int/float,最快的方法是使用 shift 进行 2/4/4 赋值。对于字节来说,最快的显然是简单的赋值。所以关于原始问题 - 分配方式是最好的。这是以最快方式使用此类函数的示例:

    public static void WriteInt(byte[] buffer, int offset, int value)
    {
        m_BytesInt[offset] = (byte)(value >> 24);
        m_BytesInt[offset + 1] = (byte)(value >> 16);
        m_BytesInt[offset + 2] = (byte)(value >> 8);
        m_BytesInt[offset + 3] = (byte) value;
    }

P.S. The tests were run on x64 environment with code compiled to cpu any (which was x64 on run) in release mode.

PS 测试在 x64 环境中运行,代码在发布模式下编译为 cpu any(运行时为 x64)。

回答by JGU

I think this might be the fastest way in C#.. (with byte array initialized to 4x the int stream w/ int32

我认为这可能是 C# 中最快的方法..(字节数组初始化为 int 流的 4 倍 w/int32

        private MemoryStream Convert(int[] Num, byte[] Bytes)
    {
        Buffer.BlockCopy(Num, 0, Bytes, 0, Bytes.Length);
        MemoryStream stream = new MemoryStream(Bytes);
        return stream;
    }

回答by fiinix

As many in here seem to argue about if BitConverteris beter than a dedicated struct. Based on BCL source code the BitConverter.GetBytes()looks like this:

正如这里的许多人似乎争论 ifBitConverter比专用struct. 基于 BCL 源代码,BitConverter.GetBytes()看起来像这样:

public static unsafe byte[] GetBytes(int value)
{
    byte[] buffer = new byte[4];
    fixed (byte* bufferRef = buffer)
    {
        *((int*)bufferRef) = value;
    }
    return buffer;
}

Which from my point of view is more clean and seems faster than making 1 integer + 4 byte assignments to a explicit struct as this one.

从我的角度来看,这比将 1 个整数 + 4 个字节分配给显式结构更干净,似乎更快。

[StructLayout(LayoutKind.Explicit)]
struct IntByte
{
  [FieldOffset(0)]
  public int IntVal;
  [FieldOffset(0)]
  public byte Byte0;
  [FieldOffset(1)]
  public byte Byte1;
  [FieldOffset(2)]
  public byte Byte2;
  [FieldOffset(3)]
  public byte Byte3;
}

new IntByte { IntVal = 10 } -> Byte0, Byte1, Byte2, Byte3.

回答by DragonSpit

Union is the fastest way of splitting an integer into bytes. Below is a complete program in which the C# optimizer can't optimize the byte splitting operation out, because each byte is summed and the sum is printed out.

Union 是将整数拆分为字节的最快方法。下面是一个完整的程序,其中 C# 优化器无法优化字节拆分操作,因为每个字节都进行了求和,然后打印出总和。

The timings on my laptop are 419 milliseconds for the Unionand 461 milliseconds for the BitConverter. The speed gain, however, is much greater.

我的笔记本电脑的定时是419毫秒为联盟461毫秒的BitConverter。然而,速度增益要大得多。

This method is used in an open source high-performance algorithms HPCsharplibrary, where the Union method provides a nice performance boost for the Radix Sort.

该方法用于开源高性能算法HPCsharp库,其中联合方法为基数排序提供了很好的性能提升。

Union is faster because it performs no bitwise masking and no bit-shift, but simply reads the proper byte out of the 4-byte integer.

Union 更快,因为它不执行按位掩码和移位,而只是从 4 字节整数中读取正确的字节。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace SplitIntIntoBytes
{
    [StructLayout(LayoutKind.Explicit)]
    struct FooUnion
    {
        [FieldOffset(0)]
        public byte byte0;
        [FieldOffset(1)]
        public byte byte1;
        [FieldOffset(2)]
        public byte byte2;
        [FieldOffset(3)]
        public byte byte3;

        [FieldOffset(0)]
        public int integer;
    }
    class Program
    {
        static void Main(string[] args)
        {
            testUnion();
            testBitConverter();

            Stopwatch Timer = new Stopwatch();

            Timer.Start();
            int sumTestUnion = testUnion();
            Timer.Stop();

            Console.WriteLine("time of Union:        " + Timer.ElapsedTicks + " milliseconds,  sum: " + sumTestUnion);

            Timer.Restart();
            int sumBitConverter = testBitConverter();
            Timer.Stop();

            Console.WriteLine("time of BitConverter: " + Timer.ElapsedTicks + " milliseconds,  sum: " + sumBitConverter);
            Console.ReadKey();
        }

        static int testBitConverter()
        {
            byte[] UnionBytes = new byte[4];
            byte[] SumOfBytes = new byte[4];
            SumOfBytes[0] = SumOfBytes[1] = SumOfBytes[2] = SumOfBytes[3] = 0;

            for (int i = 0; i < 10000; i++)
            {
                UnionBytes = BitConverter.GetBytes(i);
                SumOfBytes[0] += UnionBytes[0];
                SumOfBytes[1] += UnionBytes[1];
                SumOfBytes[2] += UnionBytes[2];
                SumOfBytes[3] += UnionBytes[3];
            }
            return SumOfBytes[0] + SumOfBytes[1] + SumOfBytes[2] + SumOfBytes[3];
        }

        static int testUnion()
        {
            byte[] UnionBytes;
            byte[] SumOfBytes = new byte[4];
            SumOfBytes[0] = SumOfBytes[1] = SumOfBytes[2] = SumOfBytes[3] = 0;

            FooUnion union = new FooUnion();

            for (int i = 0; i < 10000; i++)
            {
                union.integer = i;
                UnionBytes = new byte[] { union.byte0, union.byte1, union.byte2, union.byte3 };
                SumOfBytes[0] += UnionBytes[0];
                SumOfBytes[1] += UnionBytes[1];
                SumOfBytes[2] += UnionBytes[2];
                SumOfBytes[3] += UnionBytes[3];
            }
            return SumOfBytes[0] + SumOfBytes[1] + SumOfBytes[2] + SumOfBytes[3];
        }
    }
}