TL;DR: почти всегда лучше просто выполнить четыре широковещательных загрузки, используя _mm256_set1_pd()
. Это очень хорошо для Haswell и более поздних версий, где vbroadcastsd ymm,[mem]
не требует операции тасования ALU, и обычно также является лучшим вариантом для Sandybridge/Ivybridge (где это инструкция загрузки из 2 операций + тасование).
Это также означает, что вам вообще не нужно заботиться о выравнивании, кроме естественного выравнивания для double
.
Первый вектор будет готов раньше, чем если бы вы выполнили двухэтапную загрузку + перетасовку, поэтому потенциально неупорядоченное выполнение может начаться с кода, использующего эти векторы, в то время как первый все еще загружается. AVX512 может даже складывать широковещательные нагрузки в операнды памяти для инструкций ALU, поэтому такой способ позволит перекомпилировать небольшое преимущество AVX512 с векторами 256b.
(Обычно лучше использовать set1(x)
, а не _mm256_broadcast_sd(&x)
. Если форма регистра-источника vbroadcastsd
только для AVX2 недоступна, компилятор может выбрать сохранение -> широковещательная загрузка или два перетасовывания. означает, что ваш код будет работать на входах, которые уже находятся в регистрах.)
Если вы действительно ограничены конфликтами ресурсов порта загрузки или пропускной способности, а не общим количеством мопов или ресурсов ALU/shuffle, это может помочь заменить пару широковещательных передач 64-> 256b на широковещательную нагрузку 16B-> 32B (vbroadcastf128
/ _mm256_broadcast_
pd
) и две перетасовки на дорожке (vpermilpd
или vunpckl/hpd
(_mm256_shuffle_pd
)).
Или с AVX2: загрузите 32 байта и используйте 4 _mm256_permute4x64_pd
перетасовки для трансляции каждого элемента в отдельный вектор.
Источник таблицы insn Agner Fog (и microarch pdf):
Intel Haswell и более поздние версии:
vbroadcastsd ymm,[mem]
и другие инструкции широковещательной загрузки - это инструкции 1uop, которые полностью обрабатываются загрузочным портом (трансляция происходит "бесплатно").
Общая стоимость выполнения четырех широковещательных нагрузок таким образом составляет 4 инструкции. слитный домен: 4 мкп. unfused-domain: 4 мопса для p2/p3. Производительность: два вектора за цикл.
Haswell имеет только одно устройство тасования на порту 5. Выполнение всех ваших широковещательных нагрузок с load+shuffle будет узким местом на p5.
Максимальная пропускная способность вещания, вероятно, достигается при сочетании vbroadcastsd ymm,m64
и перетасовки:
## Haswell maximum broadcast throughput with AVX1
vbroadcastsd ymm0, [rsi]
vbroadcastsd ymm1, [rsi+8]
vbroadcastf128 ymm2, [rsi+16] # p23 only on Haswell, also p5 on SnB/IvB
vunpckhpd ymm3, ymm2,ymm2
vunpcklpd ymm2, ymm2,ymm2
vbroadcastsd ymm4, [rsi+32] # or vaddpd ymm0, [rdx+something]
#add rsi, 40
Любой из этих режимов адресации может быть двухрегистровым индексным режимом адресации, потому что не нужно микрофьюзить, чтобы быть единой uop.
AVX1: 5 векторов за 2 цикла, насыщая p2/p3 и p5. (Игнорирование разделения строк кэша при загрузке 16 байт). 6 объединенных доменов, оставляя только 2 мопов на 2 цикла для использования 5 векторов... Реальный код, вероятно, использовал бы часть пропускной способности загрузки для загрузки чего-то еще (например, нешироковещательная загрузка 32B из другого массива, возможно, как операнд памяти в инструкцию ALU), или оставить место для сохранения, чтобы украсть p23 вместо использования p7.
## Haswell maximum broadcast throughput with AVX2
vmovups ymm3, [rsi]
vbroadcastsd ymm0, xmm3 # special-case for the low element; compilers should generate this from _mm256_permute4x64_pd(v, 0)
vpermpd ymm1, ymm3, 0b01_01_01_01 # NASM syntax for 0x99
vpermpd ymm2, ymm3, 0b10_10_10_10
vpermpd ymm3, ymm3, 0b11_11_11_11
vbroadcastsd ymm4, [rsi+32]
vbroadcastsd ymm5, [rsi+40]
vbroadcastsd ymm6, [rsi+48]
vbroadcastsd ymm7, [rsi+56]
vbroadcastsd ymm8, [rsi+64]
vbroadcastsd ymm9, [rsi+72]
vbroadcastsd ymm10,[rsi+80] # or vaddpd ymm0, [rdx + whatever]
#add rsi, 88
AVX2: 11 векторов за 4 цикла, насыщение p23 и p5. (Игнорируя разбиение строки кэша для загрузки 32 байт...). Слитный домен: 12 моп, оставляя 2 моп на каждые 4 цикла сверх этого.
Я думаю, что невыровненные загрузки 32B немного более уязвимы с точки зрения производительности, чем невыровненные загрузки 16B, такие как vbroadcastf128
.
Интел СНБ/ИвБ:
vbroadcastsd ymm, m64
— это 2 объединенных домена uop: p5 (перетасовка) и p23 (загрузка).
vbroadcastss xmm, m32
и movddup xmm, m64
предназначены только для порта загрузки одиночной операции. Интересно, что vmovddup ymm, m256
также является однократной инструкцией загрузки только для порта, но, как и все загрузки 256b, она занимает порт загрузки на 2 такта. Он все еще может генерировать адрес магазина во 2-м цикле. Однако этот uarch плохо справляется с разделением строк кэша для невыровненных 32-битных загрузок. gcc по умолчанию использует movups / vinsertf128 для невыровненных 32-байтных загрузок с -mtune=sandybridge
/ -mtune=ivybridge
.
4-кратная широковещательная нагрузка: 8 объединенных доменных операций: 4 p5 и 4 p23. Пропускная способность: 4 вектора на 4 цикла, узкое место на порту 5. Множественные загрузки из одной и той же строки кэша в одном и том же цикле не вызывают конфликта между кэшем и банком, так что это и близко не к насыщению портов загрузки (также необходимо для адресов хранения). поколение). Это происходит только в одном и том же банке двух разных строк кэша в одном и том же цикле.
Множественные 2-оперативные инструкции без каких-либо других инструкций между ними являются наихудшим случаем для декодеров, если uop-кэш холодный, но хороший компилятор будет смешивать между ними однократные инструкции.
У SnB есть 2 блока тасования, но только тот, что на p5, может обрабатывать тасование, имеющее версию 256b в AVX. Использование uop целочисленного тасования p1 для передачи двойного значения обоим элементам регистра xmm ни к чему не приведет, так как vinsertf128 ymm,ymm,xmm,i
использует uop p5 тасования.
## Sandybridge maximum broadcast throughput: AVX1
vbroadcastsd ymm0, [rsi]
add rsi, 8
один за такт, насыщая p5, но используя только половину емкости p23.
Мы можем сэкономить один uop загрузки за счет еще 2 uop перемешивания, пропускная способность = два результата за 3 такта:
vbroadcastf128 ymm2, [rsi+16] # 2 uops: p23 + p5 on SnB/IvB
vunpckhpd ymm3, ymm2,ymm2 # 1 uop: p5
vunpcklpd ymm2, ymm2,ymm2 # 1 uop: p5
Выполнение загрузки 32B и распаковка с помощью 2x vperm2f128
-> 4x vunpckh/lpd
может помочь, если магазины являются частью того, что конкурирует за p23.
person
Peter Cordes
schedule
23.05.2016
_mm256_broadcast_pd
и 4 x_mm256_shuffle_pd
— вы сэкономите две загрузки, но добавите две инструкции. Хотя я сомневаюсь, что это будет иметь большое значение. - person Paul R   schedule 13.05.2014mm256_permute2f128_pd
и 4x_mm256_permute_pd
. Не знаю, лучше ли это вашего предложения. Но в узких петлях нагрузка может быть убийственной. - person Z boson   schedule 13.05.2014_mm256_broadcast_sd
) имеют еще большее преимущество, см. мой ответ. - person Peter Cordes   schedule 23.05.2016