10

必须有一种更快更好的方法来交换 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;
    }
}

有人有想法吗?

4

5 回答 5

7

在我尝试申请 Uberhacker 奖时,我提交了以下内容。对于我的测试,我使用了一个 8,192 字节的 Source 数组并调用了SwapX2100,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 倍以上。

于 2011-12-16T22:35:16.840 回答
6

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

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循环外部)。

于 2009-10-23T01:57:37.350 回答
5

我一直很喜欢这个:

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 位。

于 2011-12-16T19:51:09.603 回答
2

在我的测试中,下一个方法几乎是接受答案的 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 字节

  • 交换V2:1055、1051、1043、1041、1044
  • 交换X2:2802、2803、2803、2805、2805

五次测试,50.000.000 次 6 字节

  • 交换V2:1092、1085、1086、1087、1086
  • 交换X2: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 最小: 111
  • SwapA2 : 17, 17, 17, 17, 16 分钟: 16
于 2018-03-12T00:36:38.867 回答
1

好吧,您可以使用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];
}
于 2009-10-23T01:18:43.497 回答