Быстрое преобразование байтов от младшего к старшему в ASM - PullRequest
5 голосов
/ 31 августа 2009

У меня есть массив типов uint в C #. После проверки, работает ли программа на машине с прямым порядком байтов, я хочу преобразовать данные в тип с прямым порядком байтов. Поскольку объем данных может стать очень большим, но всегда равномерным, я думал рассмотреть два типа uint как тип ulong, для лучшей производительности и запрограммировать его в ASM, поэтому я ищу очень быстро (по возможности, самый быстрый) ) Ассемблер-алгоритм для преобразования little-endian в big-endian.

Ответы [ 3 ]

7 голосов
/ 31 августа 2009

Для большого объема данных рекомендуется использовать инструкцию bswap (доступную в Visual C ++ под _byteswap_ushort, _byteswap_ulong и _byteswap_uint64). Это даже превзойдет рукописную сборку. Они не доступны в чистом C # без P / Invoke, поэтому:

  1. Используйте это, только если у вас есть много данных для обмена байтами.
  2. Вам следует серьезно подумать о написании приложений ввода-вывода самого низкого уровня в управляемом C ++, чтобы вы могли выполнить обмен перед тем, как переносить данные в управляемый массив. Вы уже должны написать библиотеку C ++, так что терять нечего и вы обойдете все проблемы производительности, связанные с P / Invoke, для алгоритмов низкой сложности, работающих с большими наборами данных.

PS: Многие люди не знают об особенностях обмена байтами. Их производительность удивительна, вдвойне для данных с плавающей запятой, потому что они обрабатывают их как целые числа. Невозможно превзойти его без ручного кодирования загрузки регистров для каждого варианта использования подстановки байтов, и если вы попробуете это, вы, вероятно, получите больший удар в оптимизаторе, чем когда-либо.

2 голосов
/ 17 декабря 2009

Вы можете просто переосмыслить проблему, это не должно быть узким местом. Возьмите наивный алгоритм (написанный на CLI-сборке, просто для удовольствия). давайте предположим, что номер, который мы хотим, находится в местном номере 0

LDLOC 0
SHL 24
LDLOC 0
LDC.i4 0x0000ff00
SHL 8
OR
LDLOC 0
LDC.i4 0x00ff0000
SHL.UN 8
OR
LDLOC 0
SHL.UN 24
OR

Максимум 13 (x86) инструкций по сборке на число (и, скорее всего, интерпретатор будет еще умнее, если использовать умные регистры). И это не становится более наивным, чем это.

Теперь сравните это с затратами на

  • Загрузка данных (включая любые периферийные устройства, с которыми вы работаете!)
  • Манипуляция данными (например, сравнение)
  • Вывод результата (каким бы он ни был)

Если 13 инструкций на число являются значительным фрагментом вашего времени выполнения, то вы выполняете ОЧЕНЬ высокопроизводительную задачу и должны иметь свой ввод в правильном формате! Вы также, вероятно, не будете использовать управляемый язык, потому что вам нужен гораздо больший контроль над буферами данных и чем-либо еще, и без дополнительной проверки границ массива.

Если этот массив данных попадает в сеть, я ожидаю, что затраты на управление сокетами будут намного выше, чем на простое изменение порядка байтов, если оно выполняется с диска, перед тем как выполнить эту программу, подумайте о предварительном переключении.

1 голос
/ 31 августа 2009

Я думал подумать о двух уинт типы как тип ulong

Ну, это также поменяет два значения uint, что может быть нежелательно ...

Вы можете попробовать код C # в небезопасном режиме, который на самом деле может работать достаточно хорошо. Как:

public static unsafe void SwapInts(uint[] data) {
   int cnt = data.Length;
   fixed (uint* d = data) {
      byte* p = (byte*)d;
      while (cnt-- > 0) {
         byte a = *p;
         p++;
         byte b = *p;
         *p = *(p + 1);
         p++;
         *p = b;
         p++;
         *(p - 3) = *p;
         *p = a;
         p++;
      }
   }
}

На моем компьютере пропускная способность составляет около 2 ГБ в секунду.

...