在C#中将int转换为4字节的最快方法
本文关键字:字节 方法 中将 int 转换 | 更新日期: 2023-09-27 18:19:59
在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);
}
现在,我发现我的解决方案比struct
和BitConverter
都好几次。
我认为不安全可能是最快的选择,并接受这一答案,但我更喜欢使用托管选项。
使用不安全代码的字节*转换是迄今为止最快的:
unsafe static void Main(string[] args) {
int i = 0x12345678;
byte* pi = (byte*)&i;
byte lsb = pi[0];
// etc..
}
这也是BitConverter所做的,这段代码避免了创建数组的成本。
在C#中,将int转换为4字节的最快方法是什么?
使用BitConverter,它的GetBytes重载需要32位整数:
int i = 123;
byte[] buffer = BitConverter.GetBytes(i);
最快的方法是使用包含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;
}
我研究了将基本类型序列化为字节数组所需的时间。当你已经有了一个数组和偏移量,你想把数据放在哪里时,我就这样做了。我想,与理论上获得4个字节的数组相比,这真的是一个重要的情况,因为当你序列化某个东西时,它正是你所需要的。我已经发现,什么方法更快的答案取决于您想要序列化的类型。我尝试过几种方法:
- 带有额外缓冲区溢出检查的不安全引用
- GetBytes+后续Buffer.BulkCopy(这基本上与1加开销相同)
- 带班次的直接分配(
m_Bytes[offset] = (byte)(value >> 8)
- 带有移位和逐位的直接赋值&
m_Bytes[offset] = (byte)((i >> 8) & 0xFF)
所有的测试我都跑了1000万次。以下是以毫秒为单位的结果
长整型短字节浮点双精度1 29 32 31 30 29 342 209 233 220 212 208 2283 63 24 13 8 24 444 72 29 14
正如你所看到的,对于long和double,不安全的方式要快得多(无符号版本与有符号版本大致相同,因此它们不在表中)。对于short/int/float,最快的方法是2/4/4带shift的赋值。对于字节来说,最快的显然是简单的赋值。因此,对于最初的问题——分配方式是最好的。这是一个以最快的方式实现这种功能的例子:
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;
}
附言:测试是在x64环境下运行的,代码在发布模式下编译到cpu any(运行时为x64)。
注意,BitConverter可能不是下面测试显示的最快的
使用BitConverter
类,特别是采用Int32
参数的GetBytes
方法:
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 };
}
}
}
}
这里的许多人似乎在争论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.
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();
}
}
时,我的机器上的平均时间约为2770ms
[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();
}
}
平均时间约为4510ms。
我认为这可能是C#中最快的方法。。(字节数组初始化为int流w/int32 的4倍
private MemoryStream Convert(int[] Num, byte[] Bytes)
{
Buffer.BlockCopy(Num, 0, Bytes, 0, Bytes.Length);
MemoryStream stream = new MemoryStream(Bytes);
return stream;
}
Union是将整数拆分为字节的最快方法。下面是一个完整的程序,其中C#优化器无法优化字节分割操作,因为每个字节都被求和,并且总和被打印出来。
我的笔记本电脑上Union的时间为419毫秒,BitConverter的时间为461毫秒。然而,速度增益要大得多。
该方法用于开源高性能算法HPCsharp库,其中Union方法为Radix Sort提供了很好的性能提升
并集速度更快,因为它不执行逐位掩码和移位,而是简单地从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];
}
}
}