vaddps ymm0, ymm1,ymm1
на AMD Steamroller декодирует до 2 макроопсов с половиной пропускной способности vaddps xmm0, xmm1,xmm1
.
XOR-обнуление - это особый случай (без входной зависимости и на Jaguar, по крайней мере, избегает потребления записи файла физического регистра и позволяет movdqa из этого регистра, который должен быть удален при выпуске/переименовании, например, Bulldozer делает все это время даже для не-zerod regs). Но обнаружено ли это достаточно рано, что vxorps ymm0,ymm0,ymm0
все еще только декодирует до 1 макрооператора с равной производительностью до vxorps xmm0,xmm0,xmm0
? (в отличие от vxorps ymm3, ymm2,ymm1
)
Или обнаружение независимости происходит позже, после того, как уже декодируется на два раза? Кроме того, вектор xor-zeroing на процессорах AMD по-прежнему использует порт выполнения? На процессорах Intel Nehalem нужен порт, но семейство Sandybridge обрабатывает его на этапе выпуска/переименования.
Таблицы команд Agner Fog не перечисляют этот особый случай, и его руководство по микроархиву не упоминает количество uops.
Это может означать, что 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 regs для обнуления AVX512. (Хотя Intel, возможно, не захочет меняться, поскольку в настоящее время нет никакой выгоды для каких-либо процессоров Intel, только AMD. Если они когда-либо выпустят маломощный CPU, который разделяет векторы пополам, они могут. Их нынешний маломощный deisgn (Silvermont) t поддерживает AVX вообще, только SSE4.)
Единственный возможный недостаток, который я знаю в использовании инструкции AVX-128 для обнуления регистра 256b, заключается в том, что он не вызывает разминку исполнительных блоков 256b на процессорах Intel. Возможно, победить взлом C или С++, который пытается их согреть.
(256b векторных инструкций медленнее для первых циклов ~ 56k после первой команды 256b. См. раздел Skylake в Microarch 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, возможно, замените материал после цикла (syscall) с помощью ret
и вызовите функцию из функции C main()
.
Соберите с помощью nasm -felf64 vxor-zero.asm && ld -o vxor-zero vxor-zero.o
, чтобы создать статический двоичный файл. (Или используйте asm-link
script Я отправил в Q & A сборку статических/динамических двоичных файлов с/без libc).
Пример вывода на i7-6700k (Intel Skylake) на 3,9 ГГц. (IDK, почему моя машина работает только до 3,9 ГГц после того, как она простаивала несколько минут. Турбо до 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 вместо отставке).
uops_executed_thread
- unops-domain uops (порты выполнения). xor-zeroing не нуждается в процессорах Intel, так что это просто деление и ветвь, которые фактически выполняются. (Если мы изменили операнды на vxorps, чтобы он не просто обнулял регистр, например vxorps ymm2, ymm1,ymm0
, чтобы записать вывод в регистр, который следующий не читает, выполняемые uops будут соответствовать счету uop в объединенной домене. мы увидим, что предел пропускной способности составляет три vxorps за такт.)
2000 м. скомпилированные доменные модули, выпущенные в тактовых циклах 500 МБ, составляют 4,0 мкп за каждый такт: достигают теоретической максимальной пропускной способности интерфейса. 6 * 250 - 1500, поэтому эти подсчеты совпадают с декодированием Skylake vxorps ymm,ymm,ymm
до 1 fused-domain uop.
С другим количеством циклов в цикле все не так хорошо. например петля 5 мкп, выпущенная только на 3,75 мкп за такт. Я намеренно выбрал это 8 uops (когда vxorps декодирует один-уп).
Ширина выдачи Zen составляет 6 мкп за цикл, поэтому может быть лучше с разным количеством разворачивания. (См. этот Q & A для получения более коротких циклов, чей счетчик uop не кратен ширине проблемы, на картах Intel SnB-семейства).