【发布时间】:2021-08-18 01:20:54
【问题描述】:
这是另一个SSE is slower than normal code! Why? 类型的问题。
我知道有很多类似的问题,但它们似乎与我的情况不符。
我正在尝试使用 Montgomery Modular Multiplication 实现 Miller-Rabin primality test 以进行快速模运算。
我尝试以标量和 SIMD 方式实现它,结果发现 SIMD 版本慢了大约 10%。
如果有人想知道的话,[esp+16] 或 [esp+12] 指向的是 N 的模逆。
我真的很困惑,一个所谓的 1 延迟 1c 吞吐量 1uops 指令 psrldq 需要超过 3 延迟 0.5c 吞吐量 1uops pmuludq。
以下是在 Ryzen 5 3600 上运行的 Visual Studio 上的代码和运行时分析。
感谢任何关于如何改进 SIMD 代码和/或为什么它比标量代码慢的想法。
附:似乎由于某种原因,一条指令关闭了运行时分析
编辑1:对图片的评论有误,我在下面附上了一个固定版本:
;------------------------------------------
; Calculate base^d mod x
;
; eax = 1
; esi = x
; edi = bases[eax]
; ebp = d
; while d do
; if d & 1 then eax = (eax * edi) mod x
; edi = (edi*edi) mod x
; d >>= 1
; end
;------------------------------------------
标量代码:
LOOP_MODEXP:
push eax
test ebp, 1
jz @F
mul edi
mov ecx, edx
imul eax, DWORD PTR [esp+16]
mul esi
xor ebx, ebx
sub ecx, edx
cmovs ebx, esi
add ecx, ebx
mov DWORD PTR [esp], ecx
@@:
mov edx, edi
mulx ecx, edx, edi
imul edx, DWORD PTR [esp+16]
mulx eax, ebx, esi
xor ebx, ebx
sub ecx, eax
cmovs ebx, esi
add ecx, ebx
mov edi, ecx
pop eax
shr ebp, 1
jnz LOOP_MODEXP
SIMD 代码
movd xmm2, DWORD PTR [esp+12]
movd xmm3, esi
pshufd xmm2, xmm2, 0
pshufd xmm3, xmm3, 0
movd xmm1, edi
pshufd xmm1, xmm1, 0
movdqa xmm0, xmm1
pinsrd xmm0, eax, 2
LOOP_MODEXP:
movdqa xmm4, xmm0
pmuludq xmm0, xmm1
movdqa xmm1, xmm0
pmuludq xmm0, xmm2
pmuludq xmm0, xmm3
psubd xmm1, xmm0
psrldq xmm1, 4
pxor xmm0, xmm0
pcmpgtd xmm0, xmm1
blendvps xmm0, xmm3, xmm0
paddd xmm0, xmm1
movddup xmm1, xmm0
test ebp, 1
jnz @F
blendps xmm0, xmm4, 4
@@:
shr ebp, 1
jnz LOOP_MODEXP
pextrd eax, xmm0, 2
【问题讨论】:
-
why do single uops instructions like paddd/psubd/psrldq/movddup take so long to execute- 一条指令上的高样本数并不意味着它是那个特定的指令很慢。您的 CPU 出现故障和超标量,同时有许多指令在执行,样本不会很准确,需要进一步分析以了解高百分比在指令级粒度上可能意味着什么。 -
如果不深入研究算法,您的 SIMD 代码似乎通过所有
pmuludq和以下说明进行依赖,这意味着代码受延迟限制。在标量代码中情况并非如此,mulx/imul可以并行执行。其余指令成本较低,因此乘法序列对循环性能有很大贡献。 -
哦,对了
psrldq使用字节单位进行移位,我明白你做了什么。我对问题顶部发布的算法感到困惑。我认为仅使用 SIMD 并行执行两个计算不会带来太多好处,因为标量代码已经使用了两个 dep 链(ad Andrey 指出)并且不需要所有的洗牌。我不知道 VS 是如何分析代码的,但是pxor xmm0, xmm0以红色显示,并且计数很高(什么?),这真的很奇怪。 -
@stepan 我认为它被一条指令关闭了。根本不可能
pxor花费那么多。如果你这样想,那是有道理的。psrldq是罪魁祸首,而不是pxor。 -
@stepan:同意 quaver,那些像素归零计数很可能属于
psrldq。还有一种“偏斜”效应,即后来的指令受到指责。例如当计数器溢出时,一条指令正在等待一个缓慢的结果,并且中断在它退休后处理,归咎于下一条指令。 pxor-zeroing 是对 Zen 的依赖破坏,虽然它确实需要一个执行单元来写入零(不像 Sandybridge-family,它实际上和 nop 一样便宜),但除非有一些时髦的前面,否则它仍然没有显着的成本-结束效果。 (不太可能)。