Как оптимизировать обратный порядок групп битов - PullRequest
0 голосов
/ 18 февраля 2019

По сути, у меня есть 8 частей данных, по 2 бита каждая (4 состояния), которые хранятся в 16 младших битах 32-разрядного целого числа.Я хочу изменить порядок частей данных, чтобы выполнить некоторое сопоставление с образцом.

Мне дано целое число и 8 кандидатов, и мне нужно сопоставить одного из кандидатов с ссылкой.Однако подходящий кандидат может быть преобразован некоторым предсказуемым образом.

Если контрольные данные имеют вид [0,1,2,3,4,5,6,7], то возможные совпадения могутбыть в одной из этих 8 форм:

[0,1,2,3,4,5,6,7], [0,7,6,5,4,3,2,1]
[6,7,0,1,2,3,4,5], [2,1,0,7,6,5,4,3]
[4,5,6,7,0,1,2,3], [4,3,2,1,0,7,6,5]
[2,3,4,5,6,7,0,1], [6,5,4,3,2,1,0,7]

Шаблон состоит в том, что данные всегда в порядке, но их можно перевернуть и повернуть.

Я реализую это в C и MIPS.У меня оба работают, но они кажутся громоздкими.Мой текущий подход состоит в том, чтобы замаскировать каждый фрагмент из оригинала, переместить его на новую позицию и ИЛИ с новой переменной (инициализированной на 0).

Я сделал более жесткое кодирование в C:

int ref = 4941; // reference value, original order [1,3,0,1,3,0,1,0], (encoded as 0b0001001101001101)
int rev = 0;
rev |= ((ref & 0x0003) << 14) | ((ref & 0x000C) << 10) | ((ref & 0x0030) << 6) | ((ref & 0x00C0) << 2); // move bottom 8 bits to top
rev |= ((ref & 0xC000) >> 14) | ((ref & 0x3000) >> 10) | ((ref & 0x0C00) >> 6) | ((ref & 0x0300) >> 2); // move top 8 bits to bottom
// rev = 29124 reversed order [0,1,0,3,1,0,3,1], (0b0111000111000100)

Я реализовал цикл в MIPS, чтобы попытаться уменьшить статические инструкции:

        lw      $01, Reference($00) # load reference value
        addi    $04, $00, 4         # initialize $04 as Loop counter
        addi    $05, $00, 14            # initialize $05 to hold shift value
        addi    $06, $00, 3         # initialize $06 to hold mask (one piece of data)

# Reverse the order of data in Reference and store it in $02
Loop:   addi    $04, $04, -1            # decrement Loop counter
        and     $03, $01, $06       # mask out one piece ($03 = Reference & $06) 
        sllv    $03, $03, $05       # shift piece to new position ($03 <<= $05)
        or      $02, $02, $03       # put piece into $02 ($02 |= $03)
        sllv    $06, $06, $05       # shift mask for next piece
        and     $03, $01, $06       # mask out next piece (#03 = Reference & $06)
        srlv    $03, $03, $05       # shift piece to new position ($03 >>= $05)
        or      $02, $02, $03       # put new piece into $02 ($02 |= $03)
        srlv    $06, $06, $05       # shift mask back
        addi    $05, $05, -4            # decrease shift amount by 4
        sll     $06, $06, 2         # shift mask for next loop
        bne     $04, $00, Loop      # keep looping while $04 != 0

Есть ли способ реализовать это, что проще или, по крайней мере, меньше инструкций?

Ответы [ 3 ]

0 голосов
/ 19 февраля 2019

Чтобы обратить ваши биты, вы можете использовать следующий код.

static int rev(int v){
  // swap adjacent pairs of bits
  v = ((v >> 2) & 0x3333) | ((v & 0x3333) << 2);
  // swap nibbles
  v = ((v >> 4) & 0x0f0f) | ((v & 0x0f0f) << 4);
  // swap bytes
  v = ((v >> 8) & 0x00ff) | ((v & 0x00ff) << 8);
  return v;
}

Реализация MIPS состоит из 15 команд.

rev: # value to reverse in $01
     # uses $02 reg
   srli $02, $01, 2
   andi $02, $02, 0x3333
   andi $01, $01, 0x3333
   slli $01, $01, 2
   or   $01, $01, $02
   srli $02, $01, 4
   andi $02, $02, 0x0f0f
   andi $01, $01, 0x0f0f
   slli $01, $01, 4
   or   $01, $01, $02
   srli $02, $01, 8
   andi $02, $02, 0xff
   andi $01, $01, 0xff
   slli $01, $01, 8
   or   $01, $01, $02
   # result in $01

Обратите внимание, что вы можете одновременно обратить 2x16 бит, просто удвоивконстанты (и даже 4 на 64-битных машинах).Но я не уверен, что это полезно в вашем случае.

0 голосов
/ 19 февраля 2019

Примечание: Будьте осторожны с рукописной оптимизированной сборкой, там действительно есть специфичная для процессора оптимизация, держите их, если вы действительно испытываете трудности с генерацией компилятора в тесном цикле .

Вы можетеулучшите конвейер , (если вы кодируете в C, компилятор сделает это за вас) и используйте интервал задержки инструкции bne.Это улучшит ваш параллелизм на уровне команд .

Предполагается, что у вас есть что-то вроде процессора Mips с 1 слотом задержки и 5-ступенчатым конвейером (извлечение инструкций, декодирование, выполнение, память, обратная запись).

В этом конвейере вводится Чтение после записи Опасности, связанные с зависимостью от данных, чаще всего возникали в регистре $3.

Из-за риска RaW ваш конвейер зависает.

# Reverse the order of data in Reference and store it in $02
Loop:   and     $03, $01, $06       # mask out one piece ($03 = Reference & $06)
        addi    $04, $04, -1        # decrement Loop counter (RaW on $3)
        sllv    $03, $03, $05       # shift piece to new position ($03 <<= $05)
        sllv    $06, $06, $05       # shift mask for next piece
        or      $02, $02, $03       # put piece into $02 ($02 |= $03)
        and     $03, $01, $06       # mask out next piece (#03 = Reference & $06)
        srlv    $06, $06, $05       # shift mask back
        srlv    $03, $03, $05       # shift piece to new position ($03 >>= $05)
        addi    $05, $05, -4        # decrease shift amount by 4
        or      $02, $02, $03       # put new piece into $02 ($02 |= $03)
        bne     $04, $00, Loop      # keep looping while $04 != 0
        sll     $06, $06, 2         # shift mask for next loop

Если у вас процессор Superscalar, решение нуждается в некоторых изменениях.

0 голосов
/ 19 февраля 2019

Для очень простого и эффективного подхода используйте 256-байтовую таблицу поиска и выполните 2 поиска:

extern unsigned char const xtable[256];

unsigned int ref = 4149;
unsigned int rev = (xtable[ref & 0xFF] << 8) | xtable[ref >> 8];

Массив xtable можно статически инициализировать с помощью набора макросов:

#define S(x)  ((((x) & 0x0003) << 14) | (((x) & 0x000C) << 10) | \
               (((x) & 0x0030) <<  6) | (((x) & 0x00C0) <<  2) | \
               (((x) & 0xC000) >> 14) | (((x) & 0x3000) >> 10) | \
               (((x) & 0x0C00) >>  6) | (((x) & 0x0300) >>  2))
#define X8(m,n)   m((n)+0), m((n)+1), m((n)+2), m((n)+3), \
                  m((n)+4), m((n)+5), m((n)+6), m((n)+7)
#define X32(m,n)  X8(m,(n)), X8(m,(n)+8), X8(m,(n)+16), X8(m,(n)+24)

unsigned char const xtable[256] = {
    X32(S,   0), X32(S,  32), X32(S,  64), X32(S,  96),
    X32(S, 128), X32(S, 160), X32(S, 192), X32(S, 224),
};

#undef S
#undef X8
#undef X32

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...