使用16位字更快地在C#中交换字节序

必须有一个更快更好的方法来交换16位字的字节然后这个:

public static void Swap(byte[] data) { for (int i = 0; i < data.Length; i += 2) { byte b = data[i]; data[i] = data[i + 1]; data[i + 1] = b; } } 

有没有人有想法?

在我申请Uberhacker奖时,我提交以下内容。 对于我的测试,我使用了一个8,192字节的Source数组,并称为SwapX2 100,000次:

 public static unsafe void SwapX2(Byte[] source) { fixed (Byte* pSource = &source[0]) { Byte* bp = pSource; Byte* bp_stop = bp + source.Length; while (bp < bp_stop) { *(UInt16*)bp = (UInt16)(*bp << 8 | *(bp + 1)); bp += 2; } } } 

我的基准测试表明此版本比原始问题中提交的代码快1.8倍。

这种方式似乎比原始问题中的方法稍快:

 private static byte[] _temp = new byte[0]; public static void Swap(byte[] data) { if (data.Length > _temp.Length) { _temp = new byte[data.Length]; } Buffer.BlockCopy(data, 1, _temp, 0, data.Length - 1); for (int i = 0; i < data.Length; i += 2) { _temp[i + 1] = data[i]; } Buffer.BlockCopy(_temp, 0, data, 0, data.Length); } 

我的基准测试假定重复调用该方法,因此_temp数组的大小调整不是一个因素。 此方法依赖于以下事实:可以使用初始Buffer.BlockCopy(...)调用(源位置偏移1)完成一半的字节交换。

如果我完全失去理智,请自行测试一下。 在我的测试中,这个方法与原始方法(我修改为在循环之外声明byte b )一样长约70%。

我一直很喜欢这个:

 public static Int64 SwapByteOrder(Int64 value) { var uvalue = (UInt64)value; UInt64 swapped = ( (0x00000000000000FF) & (uvalue >> 56) | (0x000000000000FF00) & (uvalue >> 40) | (0x0000000000FF0000) & (uvalue >> 24) | (0x00000000FF000000) & (uvalue >> 8) | (0x000000FF00000000) & (uvalue << 8) | (0x0000FF0000000000) & (uvalue << 24) | (0x00FF000000000000) & (uvalue << 40) | (0xFF00000000000000) & (uvalue << 56)); return (Int64)swapped; } 

我相信你会发现这是最快的方法,而且相当可读和安全。 显然这适用于64位值,但相同的技术可用于32或16-。

在我的测试中,下一个方法比接受的答案快了近3倍。 (总是在超过3个字符或6个字节时更快,在少于或等于3个字符或6个字节时慢一点。)( 注意,接受的答案可以在数组范围之外读/写。

(更新虽然有一个指针,但不需要调用属性来获取长度。使用该指针要快一点,但需要运行时检查,或者如下例所示,为每个平台构建项目配置。定义X86和每种配置下的X64。)

 static unsafe void SwapV2(byte[] source) { fixed (byte* psource = source) { #if X86 var length = *((uint*)(psource - 4)) & 0xFFFFFFFEU; #elif X64 var length = *((uint*)(psource - 8)) & 0xFFFFFFFEU; #else var length = (source.Length & 0xFFFFFFFE); #endif while (length > 7) { length -= 8; ulong* pulong = (ulong*)(psource + length); *pulong = ( ((*pulong >> 8) & 0x00FF00FF00FF00FFUL) | ((*pulong << 8) & 0xFF00FF00FF00FF00UL)); } if(length > 3) { length -= 4; uint* puint = (uint*)(psource + length); *puint = ( ((*puint >> 8) & 0x00FF00FFU) | ((*puint << 8) & 0xFF00FF00U)); } if(length > 1) { ushort* pushort = (ushort*)psource; *pushort = (ushort) ( (*pushort >> 8) | (*pushort << 8)); } } } 

五次测试,300.000倍8192字节

  • SwapV2:1055,1051,1043,1041,1044
  • SwapX2:2802,2803,2803,2805,2805

五次测试用50.000.000次6个字节

  • SwapV2:1092,1085,1086,1087,1086
  • SwapX2:1018,1019,1015,1017,1018

但是,如果数据很大并且性能非常重要,您可以使用SSE或AVX。 (快13倍。) https://pastebin.com/WaFk275U

测试5次,100000个循环,8192个字节或4096个字符

  • SwapX2:226,223,225,226,227分钟:223
  • SwapV2:113,111,112,114,112 Min:111
  • SwapA2:17,17,17,17,16分钟:16

好吧,你可以使用XOR交换技巧 ,以避免中间字节。 但是,它不会更快,如果IL完全相同,我也不会感到惊讶。

 for (int i = 0; i < data.Length; i += 2) { data[i] ^= data[i + 1]; data[i + 1] ^= data[i]; data[i] ^= data[i + 1]; }