Обнуляется ли vxorps на AMD Jaguar/Bulldozer/Zen быстрее с регистрами xmm, чем ymm?

Процессоры AMD обрабатывают 256-битные инструкции AVX путем декодирования в две 128-битные операции. например vaddps ymm0, ymm1,ymm1 на AMD Steamroller декодирует до 2 макро-операций, с половиной пропускной способности vaddps xmm0, xmm1,xmm1,

Обнуление XOR является особым случаем (без входной зависимости, и на Jaguar, по крайней мере, избегает потребления записи в физическом регистре и позволяет удалить movdqa из этого регистра при выпуске / переименовании, как это делает Bulldozer все время, даже для не-Zerod регс). Но обнаружено ли это достаточно рано, чтобы vxorps ymm0,ymm0,ymm0 все еще только декодирует до 1 макрооперации с равной производительностью vxorps xmm0,xmm0,xmm0 ? (В отличие от vxorps ymm3, ymm2,ymm1 )

Или обнаружение независимости происходит позже, уже после декодирования в два мопа? Кроме того, использует ли Xor-Zero на процессорах AMD порт исполнения? На процессорах Intel Nehalem нужен порт, но семейство Sandybridge обрабатывает его на этапе выпуска / переименования.

Таблицы инструкций Агнера Фога не перечисляют этот особый случай, а в его руководстве по микроархам не упоминается количество мопов.


Это может означать vxorps xmm0,xmm0,xmm0 это лучший способ реализовать _mm256_setzero_ps(),

Для AVX512, _mm512_setzero_ps() также сохраняет байт, используя, по возможности, только идиому обнуления с VEX-кодом, а не EVEX. (т.е. для zmm0-15. vxorps xmm31,xmm31,xmm31 все равно потребуется EVEX). В настоящее время gcc/clang использует идиомы xor-zeroing любой желаемой ширины регистра, а не всегда использует AVX-128.

Сообщается как ошибка clang 32862 и ошибка gcc 80636. MSVC уже использует xmm, Пока не сообщается в ICC, которая также использует регистры zmm для обнуления AVX512. (Хотя Intel может и не захотеть меняться, так как в настоящее время нет никакой выгоды для каких-либо процессоров Intel, только для AMD. Если они когда-либо выпустят процессор с низким энергопотреблением, который разделяет векторы пополам, они могут. Их нынешний дизайн с низким энергопотреблением (Silvermont) не делает ' Поддерживает AVX вообще, только SSE4.)


Единственный возможный недостаток, который я знаю об использовании инструкции AVX-128 для обнуления регистра 256b, заключается в том, что он не запускает прогрев исполнительных блоков 256b на процессорах Intel. Возможно, победить взлом C или C++, который пытается согреть их.

(Векторные инструкции 256b медленнее для первых ~56k циклов после первой инструкции 256b. См. Раздел Skylake в pdf-файле Agner Fog). Это вероятно нормально, если звонит noinline функция, которая возвращает _mm256_setzero_ps не надежный способ разогреть исполнительные подразделения. (Тот, который все еще работает без AVX2, и избегает любых нагрузок (которые могут пропустить кеш) __m128 onebits = _mm_castsi128_ps(_mm_set1_epi8(0xff));
return _mm256_insertf128_ps(_mm256_castps128_ps256(onebits), onebits) который должен компилироваться в pcmpeqd xmm0,xmm0,xmm0 / vinsertf128 ymm0,xmm0,1, Это все еще довольно тривиально для того, что вы вызываете один раз, чтобы разогреть (или согреть) исполнительные блоки задолго до критического цикла. И если вы хотите что-то, что может быть встроено, вам, вероятно, понадобится inline-asm.)


У меня нет оборудования AMD, поэтому я не могу это проверить.

Если у кого-то есть аппаратное обеспечение AMD, но он не знает, как тестировать, используйте счетчики perf для подсчета циклов (и предпочтительно m-ops или uops или как там их называет AMD).

Это источник NASM/YASM, который я использую для тестирования коротких последовательностей:

section .text
global _start
_start:

    mov     ecx, 250000000

align 32  ; shouldn't matter, but just in case
.loop:

    dec     ecx  ; prevent macro-fusion by separating this from jnz, to avoid differences on CPUs that can't macro-fuse

%rep 6
    ;    vxorps  xmm1, xmm1, xmm1
    vxorps  ymm1, ymm1, ymm1
%endrep

    jnz .loop

    xor edi,edi
    mov eax,231    ; exit_group(0) on x86-64 Linux
    syscall

Если вы не работаете в Linux, возможно, замените материал после цикла (системный вызов выхода) на ret и вызвать функцию из C main() функция.

Собрать с nasm -felf64 vxor-zero.asm && ld -o vxor-zero vxor-zero.o сделать статический двоичный файл. (Или используйте asm-link скрипт, который я написал в Q&A о сборке статических / динамических двоичных файлов с / без libc).

Пример вывода на i7-6700k (Intel Skylake), на 3,9 ГГц. (IDK, почему моя машина разогревается только до 3,9 ГГц после нескольких минут простоя. Сразу после загрузки нормально работает Turbo до 4,2 или 4,4 ГГц). Поскольку я использую счетчики перфорации, на самом деле не имеет значения, на какой тактовой частоте работает машина. Никаких загрузок / накоплений или промахов в кеше кода не происходит, поэтому число тактовых циклов ядра для всех постоянно, независимо от того, как долго они работают.

$ alias disas='objdump -drwC -Mintel'
$ b=vxor-zero;  asm-link "$b.asm" && disas "$b" && ocperf.py stat -etask-clock,cycles,instructions,branches,uops_issued.any,uops_retired.retire_slots,uops_executed.thread -r4 "./$b"
+ yasm -felf64 -Worphan-labels -gdwarf2 vxor-zero.asm
+ ld -o vxor-zero vxor-zero.o

vxor-zero:     file format elf64-x86-64


Disassembly of section .text:

0000000000400080 <_start>:
  400080:       b9 80 b2 e6 0e          mov    ecx,0xee6b280
  400085:       66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00    data16 data16 data16 data16 data16 nop WORD PTR cs:[rax+rax*1+0x0]
  400094:       66 66 66 2e 0f 1f 84 00 00 00 00 00     data16 data16 nop WORD PTR cs:[rax+rax*1+0x0]

00000000004000a0 <_start.loop>:
  4000a0:       ff c9                   dec    ecx
  4000a2:       c5 f4 57 c9             vxorps ymm1,ymm1,ymm1
  4000a6:       c5 f4 57 c9             vxorps ymm1,ymm1,ymm1
  4000aa:       c5 f4 57 c9             vxorps ymm1,ymm1,ymm1
  4000ae:       c5 f4 57 c9             vxorps ymm1,ymm1,ymm1
  4000b2:       c5 f4 57 c9             vxorps ymm1,ymm1,ymm1
  4000b6:       c5 f4 57 c9             vxorps ymm1,ymm1,ymm1
  4000ba:       75 e4                   jne    4000a0 <_start.loop>
  4000bc:       31 ff                   xor    edi,edi
  4000be:       b8 e7 00 00 00          mov    eax,0xe7
  4000c3:       0f 05                   syscall

(ocperf.py is a wrapper with symbolic names for CPU-specific events.  It prints the perf command it actually ran):

perf stat -etask-clock,cycles,instructions,branches,cpu/event=0xe,umask=0x1,name=uops_issued_any/,cpu/event=0xc2,umask=0x2,name=uops_retired_retire_slots/,cpu/event=0xb1,umask=0x1,name=uops_executed_thread/ -r4 ./vxor-zero

 Performance counter stats for './vxor-zero' (4 runs):

        128.379226      task-clock:u (msec)       #    0.999 CPUs utilized            ( +-  0.07% )
       500,072,741      cycles:u                  #    3.895 GHz                      ( +-  0.01% )
     2,000,000,046      instructions:u            #    4.00  insn per cycle           ( +-  0.00% )
       250,000,040      branches:u                # 1947.356 M/sec                    ( +-  0.00% )
     2,000,012,004      uops_issued_any:u         # 15578.938 M/sec                   ( +-  0.00% )
     2,000,008,576      uops_retired_retire_slots:u # 15578.911 M/sec                   ( +-  0.00% )
       500,009,692      uops_executed_thread:u    # 3894.787 M/sec                    ( +-  0.00% )

       0.128516502 seconds time elapsed                                          ( +-  0.09% )

+- 0,02%, потому что я бежал perf stat -r4, так что он запускал мой двоичный файл 4 раза.

uops_issued_any а также uops_retired_retire_slots являются доменом слияния (предельное значение пропускной способности фронт-энда в 4 на тактовую частоту для семейства Skylake и Bulldozer). Счетчики почти идентичны, потому что нет ошибок в ветвях (что приводит к спекулятивно выпущенным мопам, а не к выбытию).

uops_executed_thread это неиспользуемый домен мопов (порты выполнения). xor-zeroing не нуждается в каких-либо процессорах Intel, поэтому на самом деле выполняются только команды dec и branch uops. (Если мы изменили операнды на vxorps, чтобы он не просто обнулял регистр, например vxorps ymm2, ymm1,ymm0 чтобы записать вывод в регистр, который следующий не читает, выполненные мопы будут соответствовать счетчику мопов в домене слияния. И мы увидим, что ограничение пропускной способности составляет три vxorps за такт.)

2000MUPS слитых доменов, выпущенных с тактовыми циклами 500M, - это 4,05Uops, выданных за такт: достигается теоретическая максимальная пропускная способность внешнего интерфейса. 6 * 250 = 1500, поэтому эти цифры совпадают с декодированием Skylake vxorps ymm,ymm,ymm на 1 слитый домен моп.

С другим числом мопов в цикле все не так хорошо. например, петля на 5 моп, выпущенная только за 3,75 моп за такт. Я намеренно выбрал 8 мопов (когда vxorps декодирует в однократный моп).

Ширина выпуска Zen составляет 6 мопов за цикл, поэтому он может лучше справиться с разным количеством развертываний. (См. Этот раздел вопросов и ответов для получения более подробной информации о коротких циклах, чей счетчик числа операций не кратен ширине проблемы, на примере семейства Intel SnB).

1 ответ

Решение

xor'ing регистр ymm с самим собой генерирует две микрооперации на AMD Ryzen, в то время как xor'ing регистр xmm с самим собой генерирует только одну микрооперацию. Таким образом, оптимальный способ ксероксации регистра ymm - это xor для соответствующего регистра xmm с самим собой и использование неявного расширения нуля.

Единственный процессор, который сегодня поддерживает AVX512- это Knights Landing. Он использует одну микрооперацию для записи в регистр zmm. Очень часто обрабатывать новое расширение размера вектора, разделяя его на две части. Это произошло с переходом с 64 на 128 бит и с переходом с 128 на 256 бит. Более чем вероятно, что в будущем некоторые процессоры (от AMD, Intel или любого другого производителя) разделят 512-битные векторы на два 256-битных вектора или даже четыре 128-битных вектора. Таким образом, оптимальный способ обнуления регистра zmm состоит в том, чтобы записать 128-битный регистр в себя и использовать нулевое расширение. И вы правы, 128-битная VEX-кодированная инструкция на один или два байта короче.

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

Другие вопросы по тегам