Сдвиг SSE/SIMD с размером / гранулярностью однобайтового элемента?

Как вы знаете, у нас есть ниже инструкции Shift в SIMD SSE: PSLL(WDQ) и PSRL(WDQ)

Нет никаких PSLLB инструкция, так как мы можем сдвинуть векторы 8-битных значений (одиночные байты)?

2 ответа

Решение

В особом случае сдвига влево на один, вы можете использовать paddb xmm0, xmm0 ,


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

например

    psrlw   xmm0, 2       ; doesn't matter what size (w/d/q): performance is the same for all sizes on all CPUs
    pand    xmm0, [mask_right2]

section .rodata
  align 16
    ;; required mask depends on the shift count
    mask_right2: times 16  db 0xff >> 2      (16 bytes of 0x3f)

Или передайте 0x3f в векторный регистр перед циклом другим способом, например vpbroadcastd или же vbroadcastss из мечты в памяти, SSE3 movddup от слова или просто movdqa векторная нагрузка (vpbroadcastb требует дополнительной ALU UOP, в отличие от dword или более широких трансляций, которые просто загружаются). Или генерировать на лету с такой последовательностью, как pcmpeqd xmm0,xmm0 / psrlw xmm0, 8+2 / packuswb xmm0,xmm0, При правильном выборе количества сдвигов вы можете создать любой шаблон из 2 n -1 байтов (повторяющиеся нули, а затем повторяющиеся).

mov r32, imm32 / movd xmm, r32 и shuffle также вариант, но, вероятно, не будет сохранять байты инструкции по сравнению с pcmpeqw /... последовательность. (Обратите внимание, что версия реестра-источника VBROADCASTSS только для AVX2, что здесь не имеет значения, поскольку целочисленные сдвиги 256b также только для AVX2.)

Для сдвига вектора с переменным счетом создание маски в целочисленном регистре и передача ее в вектор является одним из вариантов (используйте pshufb с нулевым регистром для передачи младшего байта или использования imul eax, eax, 0x01010101 перейти от байта к мечту movd + pshufd). Вы также можете использовать pcmpeqd метод для создания единого вектора и использования psrlw xmm0, xmm1 а потом pack или же pshufb,


Я не вижу подобного эффективного способа эмулирования арифметического сдвига вправо (несуществующего PSRAB). Старший байт каждого слова обрабатывается правильно PSRAW, Смещение младшего байта каждого слова в верхнюю позицию позволит другому PSRAW скопируйте его бит знака столько раз, сколько требуется.

; input in xmm0.  Using AVX to save on mov instructions
VPSLLDQ   xmm1, xmm0, 1      ; or VPSLLW xmm1, xmm0, 8, but this distributes one of the uops to the shuffle port
VPSRAW    xmm1, xmm1, 8+2    ; shift low bytes back to final destination

VPSRAW    xmm0, xmm0, 2      ; shift high bytes, leaving garbage in low bytes
VPBLENDVB xmm0, xmm1, xmm0, xmm2  ; (where xmm2 holds a mask of alternating 0 and -1, which could be generated with pcmpeqw / psrlw 8).  This insn is fairly slow

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


Без VPBLENDVB (возможно, даже лучше, когда он доступен, если генерирование или загрузка константы для него происходит медленно):

VPSLLDQ   xmm1, xmm0, 1      ; or VPSLLW 8
VPSRAW    xmm1, xmm1, n      ; low bytes in the wrong place

VPSRAW    xmm0, xmm0, 8+n    ; shift high bytes all the way to the bottom of the element
VPSLLW    xmm0, xmm0, 8      ; high bytes back in place, with zero in the low byte.  (VPSLLDQ can't work: PSRAW 8+n leaves garbage we need to clear)

VPSRLW    xmm1, xmm1, 8      ; shift low bytes into place, leaving zero in the high byte.  (VPSRLDQ 1 could do this, if we started with VPSLLW instead of VPSLLDQ)
VPOR      xmm0, xmm0, xmm1

Использование PAND/PANDN/POR с константой (чередование 0/-1 байтов) в регистре также будет работать (с гораздо меньшим давлением на порт сдвига) для выполнения смешивания байтов, и является лучшим выбором, если вам нужно сделать это в цикле.

Вот еще один способ эмулировать "psrab", который работает для SSE или AVX с 1 пустым регистром:

  __ punpckhbw(scratch, src);  // junk in low bytes
  __ punpcklbw(dst, src);      // junk in low bytes
  __ psraw(scratch, 8 + shift);
  __ psraw(dst, 8 + shift);
  __ packsswb(dst, scratch);   // pack words to get result
Другие вопросы по тегам