必须有一种更快更好的方法来交换 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;
}
}
有人有想法吗?
必须有一种更快更好的方法来交换 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)。
请自己对此进行基准测试,以防我完全失去理智。在我的测试中,这个方法花费的时间大约是原始方法的 70%(我对其进行了修改以声明byte b
循环外部)。
我一直很喜欢这个:
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 字节
五次测试,50.000.000 次 6 字节
但是如果数据很大并且性能真的很重要,你可以使用 SSE 或 AVX。(快 13 倍。)https://pastebin.com/WaFk275U
测试 5 次,100000 次循环,8192 字节或 4096 字符
好吧,您可以使用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];
}