Страницы

Поиск по вопросам

понедельник, 24 декабря 2018 г.

Битовый сдвиг для набора байт

Есть byte[]
Требуется выполнить для него битовый сдвиг влево или вправо с минимальными затратами.
В поиске решений я наткнулся на BitArray. Один из его конструкторов принимает массив байт. Класс позволяет выполнять различные битовые операции (AND NOT OR XOR), но не позволяет делать сдвиги.


Ответ

В результате у меня получились 2 функции:
Сдвиг влево
public static byte[] ShiftLeft(byte[] src, int val) { var res = new byte[src.Length]; var byteShift = val >> 3; if (byteShift >= src.Length) return res; val ^= byteShift << 3;
for (var i = 1; i < src.Length - byteShift; i++) res[i + byteShift] = (byte)((src[i] << val) | src[i - 1] >> 8 - val);
res[byteShift] = (byte)(src[0] << val); return res; }
И сдвиг вправо
public static byte[] ShiftRight(byte[] src, int val) { var res = new byte[src.Length]; var byteShift = val >> 3; if (byteShift >= src.Length) return res; val ^= byteShift << 3;
for (var i = byteShift; i < src.Length - 1; i++) res[i - byteShift] = (byte)((src[i] >> val) | src[i + 1] << 8 - val);
res[src.Length - byteShift - 1] = (byte)(src[src.Length - 1] >> val); return res; }
Функции "как есть" предполагают правильное использование. Для меня очень важна скорость, поэтому я не стал делать проверку на отрицательные значения. В идеале отрицательный сдвиг влево - это сдвиг вправо на то же число по модулю.
Так же можно было добавить проверки на нулевой сдвиг, на сдвиг кратный 8 и тд. Возможно, это ускорит общий процесс, но, как показала практика, такие случаи бывают реже, чем в каждом восьмом сдвиге, а каждое условие и операция добавляют дополнительное время выполнения.
И да, Regent говорил что-то про порядок байтов. Я не стал в этом разбираться, потому что тот вариант, который есть сейчас меня полностью устраивает. Если вы получите набор байт из числа типа long и выполните сдвиг моей функцией, то вы получите тот же результат, как если вы сначала выполните такой же сдвиг для числа, а затем получите из результата набор байтов. Набор байтов из числа я получаю функцией BitConverter.GetBytes(theLong)
На данный момент на моем компе мои функции выполняют 10млн сдвигов за 700-900 мс при величине сдвига 0-7 и время обратно пропорционально величине сдвига деленной на 8. То есть то же самое количество сдвигов на 8-15 выполнится быстрее, чем на 0-7, а на 16-23 еще быстрее и тд...
UPD
Указанное время на выполнение указано для Debug. В релизе все работает в 3-4 раза быстрее.

Комментариев нет:

Отправить комментарий