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

发布于 2024-12-26 01:54:00 字数 413 浏览 2 评论 0 原文

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

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

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

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);
}

现在我发现我的解决方案比 structBitConverter 好几个刻度。

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

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

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 struct and BitConverter by couple of ticks.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

伏妖词 2025-01-02 01:54:01

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

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

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

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

    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.

烟酒忠诚 2025-01-02 01:54:01

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

使用 BitConverter ,它是 GetBytes 重载采用 32 位整数:

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

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

Using a BitConverter and it's GetBytes overload that takes a 32 bit integer:

int i = 123;
byte[] buffer = BitConverter.GetBytes(i);
金橙橙 2025-01-02 01:54:01

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

  • 在定义的布局中(在字节位置 0、1、2、3 处
  • 以及从位置 0 开始的 int32。
  • 放入 4 个变量,读出字节。
  • 完成。

比 BitConverter 快得多。

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

具有必要的属性。

[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;

}

The fastest way is with a struct containing 4 bytes.

  • 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.

Significantly faster than the BitConverter.

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;

}
魄砕の薆 2025-01-02 01:54:01

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

  1. 带有额外缓冲区溢出检查的不安全引用
  2. GetBytes + 随后的 Buffer.BulkCopy (这本质上与 1 加开销相同)
  3. 使用移位直接赋值(
    m_Bytes[offset] = (byte)(value >> 8)
  4. 通过移位和按位 & 直接赋值
    m_Bytes[offset] = (byte)((i >> 8) & 0xFF)

我运行了所有测试 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          

如您所见,对于 long 和 double 来说,不安全的方式要快得多(无符号版本与其签名版本大致相同,因此它们不在表中)。对于短/整数/浮点,最快的方法是带移位的 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;
    }

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

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:

  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)

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

      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:

    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.

暖心男生 2025-01-02 01:54:01

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

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

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

您可以使用BitConverter.IsLittlEndian 根据 CPU 架构确定字节顺序。


编辑:由于编译器优化,下面的测试并不是结论性的。


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 };

            }
        }
    }
}

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

Use the BitConverter class, specifically the GetBytes method that takes an Int32 parameter:

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

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


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 };

            }
        }
    }
}
帅的被狗咬 2025-01-02 01:54:01

正如这里的许多人似乎在争论 BitConverter 是否比专用的 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;
}

从我的角度来看,这比对显式结构进行 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.

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

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.

[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.
伏妖词 2025-01-02 01:54:01
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();
    }
}

在我的机器上平均约为 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();
    }
}

平均约为 4510 毫秒。

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

[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.

单身狗的梦 2025-01-02 01:54:01

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

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

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

        private MemoryStream Convert(int[] Num, byte[] Bytes)
    {
        Buffer.BlockCopy(Num, 0, Bytes, 0, Bytes.Length);
        MemoryStream stream = new MemoryStream(Bytes);
        return stream;
    }
苯莒 2025-01-02 01:54:01

联合是将整数拆分为字节的最快方法。下面是一个完整的程序,其中 C# 优化器无法优化字节拆分操作,因为每个字节都会被求和并打印出总和。

我的笔记本电脑上的计时为Union 为 419 毫秒BitConverter 为 461 毫秒。然而,速度增益要大得多。

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

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];
        }
    }
}

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.

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

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

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.

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];
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文