Вы не добьетесь большего успеха с точки зрения задержки или числа UOP на последних Intel или AMD (я в основном смотрел таблицы Агнера Фога для Ryzen / Skylake).movq+movq+punpcklqdq
- также 3 мопа для одного и того же порта.
В Intel / AMD хранение регистров GP во временном расположении и перезагрузка их с 16-байтовым чтением может стоить учитывать для пропускной способности.если окружающие узкие места кода на порте ALU для целого числа -> вектор, который является портом 5 для недавнего Intel.
На Intel, pinsrq x,r,imm
равен 2 моп для порта 5, а movq xmm,r64
также 1 моп для порта5.
movhps xmm, [mem]
может микропереключить нагрузку, но для нее все еще требуется порт 5 ALU uop.Таким образом, movq xmm0,rax
/ mov [rsp-8], rdx
/ movhps xmm0, [rsp-8]
- это 3 мопа с плавким доменом, 2 из которых нуждаются в порте 5 на последнем Intel.Задержка пересылки магазина делает эту задержку значительно более высокой, чем вставка.
Хранение обоих регистров GP с store / store / movdqa
(задержка пересылки из магазина при чтении двух более узких хранилищ с большей нагрузкой)также 3 мопа, но это единственная разумная последовательность, которая избегает любых мопов 5 порта.~ 15 циклов задержки настолько велики, что при выполнении вне порядка могут возникнуть проблемы с его сокрытием.
Для YMM и / или более узких элементов более целесообразно рассмотреть вопрос о сохранении + перезагрузке, поскольку вы амортизируетеСтойло над большим количеством магазинов / это спасает вас больше случайностей.Но это все равно не должно быть вашей стратегией перехода к 32-битным элементам.
Для более узких элементов было бы неплохо, если бы существовал единственный способ упаковки двух узких целых чисел в 64-битную.целочисленный регистр, поэтому для более широких передач в регистры XMM.Но нет: Упаковка двух DWORD в QWORD для экономии пропускной способности магазина shld
- 1 моп в семействе Intel SnB, но для этого требуется один из входов в верхней части регистра.В x86 есть довольно слабые инструкции вставки / извлечения битового поля по сравнению с PowerPC или ARM, требующие нескольких инструкций на объединение (кроме сохранения / перезагрузки и пропускной способности хранилища 1 за такт может легко стать узким местом).
В соответствии с таблицей из http://instlatx64.atw.hu/ (получение данных о моп из IACA), стоит только 1 порт 5 моп для передачи целочисленного регистра любой ширины в вектор ax / y / zmm на Skylake-AVX512.
Агнер, похоже, не тестировал целочисленные исходные регистры на KNL, но похожеVPBROADCASTMB2Q v,k
(источник регистра маски) равен 1 моп.
С уже настроенным регистром маски: всего 2 мопа :
; k1 = 0b0010
vmovq xmm0, rax ; 1 uop p5 ; AVX1
vpbroadcastq xmm0{k1}, rdx ; 1 uop p5 merge-masking
I думаю, маскирование слиянием "бесплатно" даже для ALU Uops.Обратите внимание, что сначала мы делаем VMOVQ, чтобы избежать более длинной кодировки EVEX для него.Но если у вас есть 0001
в маске reg вместо 0010
, смешайте его в немаскированную трансляцию с vmovq xmm0{k1}, rax
.
Если настроено больше регистров маски, мы можем сделать 1 рег вuop:
vmovq xmm0, rax 2c latency
vpbroadcastq xmm0{k1}, rdx ; k1 = 0b0010 3c latency
vpbroadcastq ymm0{k2}, rdi ; k2 = 0b0100 3c latency
vpbroadcastq ymm0{k3}, rsi ; k3 = 0b1000 3c latency
(Для полного вектора ZMM, возможно, начните 2-ую цепочку dep и vinserti64x4
, чтобы объединить 256-битные половины. Также означает только 3 k регистров вместо 7. Это стоит1 дополнительный случайный переход, но если нет какой-либо программной конвейеризации, у OoO exec может возникнуть проблема с сокрытием задержки 7 слияний = 21c, прежде чем вы что-либо сделаете с вектором.)
; high 256 bits: maybe better to start again with vmovq instead of continuing
vpbroadcastq zmm0{k4}, rcx ; k4 =0b10000 3c latency
... filling up the ZMM reg
Заданная задержка Intel для vpbroadcastq
на SKX по-прежнему 3c, даже если пункт назначения только xmm, согласно электронной таблице Instlatx64, которая цитирует этот и другие источники.http://instlatx64.atw.hu/
В том же документе перечислено vpbroadcastq xmm,xmm
с задержкой 1c, поэтому, вероятно, правильно, что мы получаем задержку 3c за шаг в цепочке зависимостей слияния.К сожалению, для мопов, маскирующих слияния, регистр назначения должен быть готов уже на других входах;поэтому объединяемая часть операции не может пересылаться отдельно.
Начиная с k1 = 2 = 0b0010
, мы можем инициировать остальное с помощью KSHIFT :
mov eax, 0b0010 = 2
kmovw k1, eax
KSHIFTLW k2, k1, 1
KSHIFTLW k3, k1, 2
# KSHIFTLW k4, k1, 3
# ...
KSHIFT работает только через порт 5 (SKX), но также и KMOV;перемещение каждой маски из целочисленных регистров будет стоить дополнительных инструкций, чтобы сначала настроить целочисленные регистры.
На самом деле все в порядке, если верхние байты вектора заполнены широковещательными сообщениями, а не нулями, поэтому мы могли бы использовать 0b1110 / 0b1100 и т. Д. Для масок.
Мы в конечном итоге запишем все элементы.Мы могли бы начать с KXNOR k0, k0,k0
, чтобы сгенерировать -1 и сдвиг влево, но это 2 port5 uops против mov eax,2
/ kmovw k1, eax
при p0156 + p5.
Безрегистр маски : (Там нет kmov k1, imm
, а загрузка из памяти стоит несколько моп, поэтому в качестве одноразовой опции нет 3-х моп с использованием маскирования слиянием. Но в цикле, если вы можете сэкономить некоторые регистры маскиэто выглядит как намного лучше.)
VPBROADCASTQ xmm1, rdx ; 1 uop p5 ; AVX512VL (ZMM1 for just AVX512F)
vmovq xmm0, rax ; 1 uop p5 ; AVX1
vpblendd xmm0, xmm0, xmm1, 0b1100 ; 1 uop p015 ; AVX2
; SKX: 3 uops: 2p5 + p015
; KNL: 3 uops: ? + ? + FP0/1
Единственное преимущество здесь в том, что одному из 3 мопов не нужен порт 5.
vmovsd xmm1, xmm1, xmm0
будет также смешивать две половины, но будет работать только на 5-м порте недавнего Intel, в отличие от целочисленного немедленного наложения, который работает на любом векторном порте ALU.
Подробнее о целочисленных -> векторных стратегиях
gcc любит хранить / перезагружать, что не оптимально ни для чего, кроме как в очень редких ситуациях с 5-ю портами, когда большая задержка не имеет значения.Я подал https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80820 и https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80833, с дополнительным обсуждением того, что может быть оптимальным для различных архитектур для 32-разрядных или 64-разрядных элементов.
Я предложил вышеуказанную замену vpbroadcastq
для вставки с AVX512 при первой ошибке.
(При компиляции _mm_set_epi64x
обязательно используйте -mtune=haswell
или что-то недавнее, чтобы избежать дрянной настройки по умолчанию mtune=generic
. Или используйте -march=native
, если вашисполняемые файлы будут работать только на локальном компьютере.)