Установите регистр XMM на повторяющийся байтовый шаблон (широковещательный постоянный байт)

Я знаю, что мы можем сделать что-то вроде этого, чтобы переместить символ в регистр xmm:

movaps xmm1, xword [.__0x20]

align 16
.__0x20 db 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20

но поскольку это процесс памяти, я хочу знать, есть ли лучший способ? (также я говорю о SSE2, а не о других типах SIMD ...)

я хочу, чтобы каждый байт регистра xmm1 был 0x20, а не только один байт ..

(Примечание редактора: это можно назвать трансляцией или splat.
Это то, что делает внутренний _mm_set1_epi8(0x20).)


person ELHASKSERVERS    schedule 29.03.2020    source источник
comment
То, что вы делаете, - это самый быстрый способ сделать это, когда желаемый байт является константой.   -  person fuz    schedule 29.03.2020
comment
Я просто ищу лучший способ (если есть) какой-то немедленный или (из реестра) способ!   -  person ELHASKSERVERS    schedule 29.03.2020
comment
Ваш байт постоянный или переменный? Если это константа, то то, что вы делаете, уже является самым быстрым способом.   -  person fuz    schedule 30.03.2020
comment
это постоянный байт   -  person ELHASKSERVERS    schedule 30.03.2020
comment
В этом случае ваш код уже идеален. В зависимости от того, какой ассемблер вы используете, может быть какая-то директива times или dup, чтобы упростить ввод. Вы также можете определить макрос, если это вас раздражает.   -  person fuz    schedule 30.03.2020


Ответы (1)


При использовании только SSE2 загрузка полного шаблона из памяти, как правило, является лучшим вариантом.

В исходном коде NASM вы можете использовать times 16 db 0x20 для облегчения обслуживания.


С SSE3 вы можете выполнять 8-байтовые широковещательные загрузки с помощью movddup. С AVX вы можете выполнить 4-байтовую широковещательную загрузку с помощью vbroadcastss. Эти широковещательные нагрузки очень хороши для современных ЦП, работают на только порте загрузки и не нуждаются в перетасовке. т.е. они так же дешевы, как movaps на ЦП, которые поддержите их, за исключением байта или еще двух кодовых размеров. То же самое для регистров vbroadcastf128 в YMM.

Большинство компиляторов, кажется, не понимают этого и будут распространять константу через _mm_set1, даже если это приводит к 32-байтовой константе вместо 4 байтов, даже если просто mov... загружает ее перед циклом, а не складывает ее в операнд памяти для инструкция ALU. (И это все еще возможно с широковещательной загрузкой, когда доступен AVX512.) Clang иногда действительно использует широковещательную загрузку для простых констант.

AVX2 добавляет vpbroadcastb/w/d/q, но только dword и qword являются чисто загрузочными мопами. Для широковещательной загрузки байтов и слов требуется перетасовка uop ALU, поэтому для постоянных байтовых шаблонов вы, вероятно, захотите просто широковещательно загрузить двойное слово, которое повторяет байт 4 раза. (Если это не элемент из большой поисковой таблицы, тогда сжимайте таблицу, используя байтовую или словесную широковещательную загрузку, или pmovsx знак-расширяющую нагрузку, или что-то еще).

AVX512 добавляет vpbroadcastb/w/d/e из целочисленного регистра, чтобы вы могли mov eax, 0x20202020 / vpbroadcastd xmm0, eax, если у вас AVX512VL.


С SSE2 потребовалось бы как минимум 2 инструкции, включая перемешивание ALU, как это, и, возможно, оно того не стоит.

    movd    xmm0, [const_4B]
    pshufd  xmm0, xmm0, 0

Некоторые повторяющиеся константы могут быть сгенерированы "на лету" в паре инструкций, начиная с "все единицы" из pcmpeqd xmm0,xmm0. См. Какая лучшая инструкция последовательности для генерации векторных констант на лету? и руководство Агнера Фога.

Этот шаблон нелегко создать. Это байтовый шаблон (не слово, двойное слово или qword), и сдвиги SSE доступны только с детализацией по словам в лучшем случае. Однако, если мы знаем, что биты, смещенные по границам байтов, равны 0, все в порядке. например

   pcmpeqd  xmm0, xmm0     ; set1( -1 )
   pabsb    xmm0, xmm0     ; set1_epi8(1)    SSSE3
   pslld    xmm0, 5        ; set1_epi8(1<<5)

; or with only SSE2, something even less efficient like shift / packsswb / shift

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

person Peter Cordes    schedule 29.03.2020
comment
Знаете ли вы какие-либо ответы на этот вопрос для 64-битного регистра GP? - person Noah; 13.03.2021
comment
@Noah: Для константы обычно просто mov rdi, 0x0101010101010101 или что-то еще. Для непостоянного значения imul rcx, rdi с этой повторяющейся константой 0x01 после расширения байта нулями до RCX. Таким образом, стоимость наихудшего случая составляет mov reg,imm64 для множителей, movzx ecx, byte source и imul r64,r64. - person Peter Cordes; 13.03.2021
comment
Ага, намного умнее, чем сдвиг! - person Noah; 13.03.2021
comment
@Noah: да, быстрыми аппаратными умножителями можно злоупотреблять, чтобы делать много изящных вещей, включая суммирование достаточно мелких элементов в старший байт. (Как подсчитать количество установленных битов в 32-битном целом числе?). Умножение представляет собой операцию сдвига и сложения, при которой сложение или не контролируется битами другого значения. Также @ phuclv прекрасно объясняет механику в Как создать байт из 8 значений типа bool (и наоборот)? - person Peter Cordes; 13.03.2021