问题描述
我只是想知道下面的代码:
mov eax,r9d ; eax = j
mul n ; eax = n * j
shl eax,2 ; eax = 4 * n * j
; Now I want to 'broadcast' this to YMM,like so:
; ymm = { eax,eax,eax }
; This requires AVX512,not just AVX2
; vpbroadcastd ymm7,eax
movd xmm7,eax ; therefore I must do this workaround?
vpbroadcastd ymm7,xmm7 ; and finally,the result
可以通过某种方式简化或优化吗?
解决方法
是的,对于Intel和AMD CPU,如果没有AVX512,vmovd
+ vpbroadcastd
是正常的方法。
我看到2种优化方法:
将mul n
替换为imul r9d,n
,因为无论如何您都没有使用EDX高倍结果的一半。 2-operand imul r32,r/m32
是所有现代CPU上的单个uop; mul r/m32
需要多个。 https://uops.info/ https://agner.org/optimize/。 (当然,如果n
是立即数,则imul eax,r9d,n*4
。)
movd xmm7,eax
上 使用VEX前缀。即 vmovd xmm7,eax
。如果在传统SSE movd
写入xmm7时,任何YMM寄存器的上半部分都脏了,则会在Haswell和Ice Lake上触发AVX-SSE过渡惩罚。 (Why is this SSE code 6 times slower without VZEROUPPER on Skylake?包含有关HSW / ICL和SKL使用的不同策略的详细信息。)
没有AVX512,是的,它需要uop(如movd
指令)将数据从GP整数域传输到SIMD域,并且该uop也无法广播。然后,您需要另一个uop进行随机播放。
@chtz指出,如果英特尔CPU后端上的端口5压力是主要瓶颈,包括这样的循环(而不是总的前端操作或等待时间),您可以mov
存储(例如到堆栈)并重新加载vpbroadcastd
。
vmovd xmm,r32
和vpbroadcastd
只能在Intel CPU的端口5上运行。但是商店是微融合的p237 + p4,并且广播负载(32位或更大元素)完全在负载端口中处理,而不需要ALU uop,因此总成本仍然是2前端uop英特尔CPU,成本为p237+p4 + p23
。代替2p5
。 大约5或6个周期的存储转发延迟实际上类似于1到3个周期vmovd
+ 3个周期vpbroadcastd
,因此对于32位和如果对加载/存储端口的压力不大,则可以通过寄存器进行64位广播。
(可能包括SSE3 movddup
广播加载到XMM寄存器中,尽管车道内改组只有1个周期的延迟,所以movd + xmm改组在Haswell及更高版本上仅约4个周期的延迟。)
测量movd xmm,r
/ movd r,xmm
往返的延迟很容易,但是很难弄清楚哪个指令具有哪个延迟。在Skylake上,它们可能只是1个周期的ALU加上1个周期的旁路延迟。 Haswell显然在每个方向上都有1个循环movd
。 https://uops.info/只是通过将延迟放入一个带有创建循环承载的依赖关系的指令的循环中,并假设其他循环具有1个周期的延迟,来测量延迟的上限。 https://agner.org/optimize/猜测如何分配一对指令的延迟。也许通过将一个方向的存储转发和另一个方向的ALU传输包括在内可以做得更好,但是众所周知,Sandybridge系列的存储转发延迟是可变的,如果您不立即尝试重新加载,则传输速度会更快。 (例如,无用的商店可以加快通过商店转发瓶颈的关键路径。Adding a redundant assignment speeds up code when compiled without optimization)。并且不能假定整数存储与vmovd xmm
重装之间的存储转发与整数重装具有相同的延迟。
Skylake的movd
xmm eax往返行程总共有4个周期的延迟,而Sandybridge / Haswell中只有2个周期。可能是2和2带有旁路延迟,或者是1和3而没有告诉我们哪个方向更慢。
禅的是6个循环,所以单程可能是3个循环。
AVX512F vpbroadcastd ymm,r32
是单-uop(端口5),因此,如果您有AVX512,那就非常好。