Более быстрый способ поменять континент в С# с 16-битными словами

Должен быть более быстрый и лучший способ поменять байты 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;
    }
}

Есть ли у кого-нибудь идеи?

Ответ 1

В моей попытке подать заявку на награду Uberhacker я представляю следующее. Для моего тестирования я использовал исходный массив из 8 192 байтов и назвал 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 раза быстрее, чем код, представленный в исходном вопросе.

Ответ 2

Этот способ выглядит немного быстрее, чем метод в исходном вопросе:

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 вне цикла).

Ответ 3

Мне всегда это нравилось:

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

Ответ 4

Ну, вы можете использовать XOR swapping трюк, чтобы избежать промежуточного байта. Однако это будет не так быстро, и я не удивлюсь, если 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];
}