Загрузка xmm из регистров GP - PullRequest
0 голосов
/ 10 июня 2018

Допустим, у вас есть значения в rax и rdx, которые вы хотите загрузить в регистр xmm.

Один из способов был бы:

movq     xmm0, rax
pinsrq   xmm0, rdx, 1

Хотя это довольно медленно!Есть ли лучший способ?

1 Ответ

0 голосов
/ 10 июня 2018

Вы не добьетесь большего успеха с точки зрения задержки или числа 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 за такт может легко стать узким местом).


AVX512F может транслировать на вектор с целочисленным регистром , а маскирование слиянием допускает вставки с одним мопом.

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

...