Подтвердить что ты не робот

Как я могу отразить байт в Delphi?

Есть ли простой способ битового отражения байтовой переменной в Delphi, так что старший бит (MSB) получает младший значащий бит (LSB) и наоборот?

4b9b3361

Ответ 1

В коде вы можете сделать это следующим образом:

function ReverseBits(b: Byte): Byte;
var 
  i: Integer;
begin
  Result := 0;
  for i := 1 to 8 do
  begin
    Result := (Result shl 1) or (b and 1);
    b := b shr 1;
  end;
end;

Но таблица поиска будет намного более эффективной и будет потреблять только 256 байт памяти.

function ReverseBits(b: Byte): Byte; inline;
const
  Table: array [Byte] of Byte = (
    0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,
    8,136,72,200,40,168,104,232,24,152,88,216,56,184,120,248,
    4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,
    12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,
    2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,
    10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,
    6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,
    14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,
    1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,
    9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,
    5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,
    13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253,
    3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,
    11,139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,
    7,135,71,199,39,167,103,231,23,151,87,215,55,183,119,247,
    15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255
  );
begin
  Result := Table[b];
end;

Это более чем в 10 раз быстрее, чем версия кода, которая работает с отдельными битами.


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

Вы приняли ответ @Arioch в то время, когда он содержал тот же код Pascal, что и в этом ответе, вместе с двумя версиями ассемблера. Оказывается, эти версии ассемблера намного медленнее, чем версия Pascal. Они в два раза медленнее, чем код Pascal.

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

Что особенно важно для использования ассемблера здесь, так это то, что это настолько очевидно, что решение на основе таблицы будет чрезвычайно быстрым. Трудно представить, как это можно было бы значительно улучшить.

Ответ 2

function ByteReverseLoop(b: byte): byte;
var i: integer;
begin
  Result := 0; // actually not needed, just to make compiler happy

  for i := 1 to 8 do
  begin
    Result := Result shl 1; 
    if Odd(b) then Result := Result or 1;
    b := b shr 1;
  end;
end;

Если скорость важна, вы можете использовать таблицу поиска. Вы чувствуете это один раз при запуске программы, а затем вы просто берете значение из таблицы. Так как вам нужно всего лишь байт байт, это займет 256x1 = 256 байт памяти. И учитывая, что последние версии Delphi поддерживают встроенные функции, которые обеспечивали бы скорость, читаемость и надежность (инкапсулирующий поиск массива в функции, вы можете быть уверены, что не измените значения из-за некоторой опечатки)

Var ByteReverseLUT: array[byte] of byte;

function ByteReverse(b: byte): byte; inline;
begin Result := ByteReverseLUT[b] end;

{Unit/program initialization}
var b: byte;
    for b := Low(ByteReverseLUT) to High(ByteReverseLUT) 
        do ByteReverseLUT[b] := ByteReverseLoop(b);

Сравнение скорости нескольких реализаций, упомянутых на этом форуме.
 AMD Phenom2 x710/Win7 x64/Delphi XE2 32-бит {$ O +}

Pascal AND original:       12494
Pascal AND reversed:       33459
Pascal IF original:        46829
Pascal IF reversed:        45585

       Asm SHIFT 1:        15802
       Asm SHIFT 2:        15490
       Asm SHIFT 3:        16212

         Asm AND 1:        19408
         Asm AND 2:        19601
         Asm AND 3:        19802

Паскаль И развернут: 10052   Asm Shift развернут: 4573          LUT, названный: 3192  Математика Паскаля, названная: 4614

http://pastebin.ca/2304708

Примечание. Временные диаграммы LUT (lookup table), вероятно, довольно оптимистичны. Из-за работы в узком цикле вся таблица была втянута в кеш процессора L1. В реальных вычислениях эта функция, скорее всего, будет называться гораздо реже, а кеш L1 не будет полностью хранить таблицу.


Результат вызывных функций Pascal в подстроке - фиктивные - Delphi не вызывал их, обнаружив, что у них не было побочных эффектов. Но смешно - тайминг был другим.

      Asm Shift unrolled:         4040
             LUT, called:         3011
            LUT, inlined:          977
         Pascal unrolled:        10052
  Pas. unrolled, inlined:          849
     Pascal math, called:         4614
    Pascal math, inlined:         6517

И ниже объяснения:

Project1.dpr.427: d := BitFlipLUT(i)
0044AC45 8BC3             mov eax,ebx
0044AC47 E89CCAFFFF       call BitFlipLUT

Project1.dpr.435: d := BitFlipLUTi(i)

Project1.dpr.444: d := MirrorByte(i);
0044ACF8 8BC3             mov eax,ebx
0044ACFA E881C8FFFF       call MirrorByte

Project1.dpr.453: d := MirrorByteI(i);
0044AD55 8BC3             mov eax,ebx

Project1.dpr.460: d := MirrorByte7Op(i);
0044ADA3 8BC3             mov eax,ebx
0044ADA5 E8AEC7FFFF       call MirrorByte7Op

Project1.dpr.462: d := MirrorByte7OpI(i);
0044ADF1 0FB6C3           movzx eax,bl

Все вызовы встроенных функций были устранены. Однако про передачу параметров Delphi было принято три разных решения:

  • Для первого вызова он устраняет параметр, передаваемый вместе с вызовом функции
  • Для второго вызова он продолжал передачу параметров, несмотря на то, что функция не вызывалась
  • Для 3-го вызова он продолжал изменять передачу параметров, которая оказалась дольше, чем вызов функции! Weird!: -)

Ответ 3

function BitFlip(B: Byte): Byte;
const
  N: array[0..15] of Byte = (0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15);
begin
  Result := N[B div 16] or N[B mod 16] shl 4;
end;

Ответ 4

Использование грубой силы может быть простым и эффективным.

Эта подпрограмма НЕ в с решением David LUT.

Обновление

Добавлен массив байтов в качестве входных данных и результат, присвоенный массиву байтов. Это показывает лучшую производительность для решения LUT.

function MirrorByte(b : Byte) : Byte;  inline;
begin
  Result :=
    ((b and $01) shl 7) or
    ((b and $02) shl 5) or
    ((b and $04) shl 3) or
    ((b and $08) shl 1) or
    ((b and $10) shr 1) or
    ((b and $20) shr 3) or
    ((b and $40) shr 5) or
    ((b and $80) shr 7);
end;

Обновление 2

Поймать немного, нашел BitReverseObvious.

function MirrorByte7Op(b : Byte) : Byte;  inline;
begin
  Result :=
    {$IFDEF WIN64}  // This is slightly better in x64 than the code in x32
    (((b * UInt64($80200802)) and UInt64($0884422110)) * UInt64($0101010101)) shr 32;
    {$ENDIF}
    {$IFDEF WIN32}
    ((b * $0802 and $22110) or (b * $8020 and $88440)) * $10101 shr 16;
    {$ENDIF}
end;

Этот подход ближе к решению LUT, даже быстрее в одном тесте.

Подводя итог, MirrorByte7Op() на 5-30% медленнее, чем LUT, в 3 тестах, на 5% быстрее в одном тесте.

Код для сравнения:

uses
  System.Diagnostics;

const 
  cBit : Byte = $AA;
  cLoopMax = 1000000000;
var
  sw : TStopWatch;
  arrB : array of byte;
  i : Integer;

begin
  SetLength(arrB,cLoopMax);
  for i := 0 TO Length(arrB) - 1 do
    arrB[i]:= System.Random(256);

  sw := TStopWatch.StartNew;
  for i := 0 to Pred(cLoopMax) do
  begin
    b := b;
  end;
  sw.Stop;
  WriteLn('Loop             ',b:3,' ',sw.ElapsedMilliSeconds);

  sw := TStopWatch.StartNew;
  for i := 0 to Pred(cLoopMax) do
  begin
    b := ReflectBits(arrB[i]); 
  end;
  sw.Stop;
  WriteLn('RB array in:     ',b:3,' ',sw.ElapsedMilliSeconds);

  sw := TStopWatch.StartNew;
  for i := 0 to Pred(cLoopMax) do
  begin
    b := MirrorByte(arrB[i]);
  end;
  sw.Stop;
  WriteLn('MB array in:     ',b:3,' ',sw.ElapsedMilliSeconds);

  sw := TStopWatch.StartNew;
  for i := 0 to Pred(cLoopMax) do
  begin
    b := MirrorByte7Op(arrB[i]);
  end;
  sw.Stop;
  WriteLn('MB7Op array in : ',arrB[0]:3,' ',sw.ElapsedMilliSeconds);

  sw := TStopWatch.StartNew;
  for i := 0 to Pred(cLoopMax) do
  begin
    arrB[i] := ReflectBits(arrB[i]);
  end;
  sw.Stop;
  WriteLn('RB array in/out: ',arrB[0]:3,' ',sw.ElapsedMilliSeconds);

  sw := TStopWatch.StartNew;
  for i := 0 to Pred(cLoopMax) do
  begin
    arrB[i]:= MirrorByte(arrB[i]);
  end;
  sw.Stop;
  WriteLn('MB array in/out: ',arrB[0]:3,' ',sw.ElapsedMilliSeconds);

  sw := TStopWatch.StartNew;
  for i := 0 to Pred(cLoopMax) do
  begin
    arrB[i]:= MirrorByte7Op(arrB[i]);
  end;
  sw.Stop;
  WriteLn('MB7Op array in/out: ',arrB[0]:3,' ',sw.ElapsedMilliSeconds);

  ReadLn;

end.

Результат теста (XE3, i7 CPU 870):

                                32 bit     64 bit
--------------------------------------------------
Byte assignment (= empty loop)   599 ms    2117 ms
MirrorByte    to byte, array in 6991 ms    8746 ms
MirrorByte7Op to byte, array in 1384 ms    2510 ms
ReverseBits   to byte, array in  945 ms    2119 ms
--------------------------------------------------
ReverseBits   array in/out      1944 ms    3721 ms
MirrorByte7Op array in/out      1790 ms    3856 ms
BitFlipNibble array in/out      1995 ms    6730 ms
MirrorByte    array in/out      7157 ms    8894 ms
ByteReverse   array in/out     38246 ms   42303 ms

Я добавил некоторые другие предложения в последнюю часть таблицы (все вложенные). Вероятно, наиболее справедливо проверить в цикле с массивом и массивом. ReverseBits (LUT) и MirrorByte7Op сравнимы по скорости, за которыми следует BitFlipNibble (LUT), которая хуже соответствует бит x64.

Примечание. Я добавил новый алгоритм для 64-разрядной части x64 MirrorByte7Op. Он лучше использует 64-битные регистры и имеет меньше инструкций.