SSE/SIMD-сдвиг с размером байта одного байта?

Как вы знаете, мы имеем ниже инструкции Shift в SIMD SSE: PSLL (W-D-Q) и PSRL (W-D-Q)

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

Ответ 1

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


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

например.

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

section .rodata
    ;; required mask depends on the shift count
    mask1 dd 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f

Или передайте 0x7f в векторный регистр перед циклом каким-то другим способом, например vpbroadcastb из одного байта, или сгенерируйте "на лету" с такой последовательностью, как pcmpeqw xmm0,xmm0/psrlw xmm0, 9/packuswb xmm0,xmm0. При правильном выборе количества сдвигов вы можете сгенерировать любой шаблон из 2 n -1 байтов (повторяющиеся нули, а затем повторяющиеся).

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


Я не вижу такого же эффективного способа эмулировать арифметический сдвиг вправо (несуществующий 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 байтами) в регистре также будет работать (с гораздо меньшим давлением на порт сдвига) для выполнения байтовой комбинации, и это лучший выбор, если вы должны сделать это в цикле.