使用pxor和xorps的混合会影响性能吗?

Ale*_*hak 6 x86 assembly sse simd

我遇到了使用PCLMULQDQ实现快速CRC计算.我看到,那些人混合pxorxorps说明很像下面的片段:

movdqa  xmm10, [rk9]
movdqa  xmm8, xmm0
pclmulqdq xmm0, xmm10, 0x11
pclmulqdq xmm8, xmm10, 0x0
pxor  xmm7, xmm8
xorps xmm7, xmm0

movdqa  xmm10, [rk11]
movdqa  xmm8, xmm1
pclmulqdq xmm1, xmm10, 0x11
pclmulqdq xmm8, xmm10, 0x0
pxor  xmm7, xmm8
xorps xmm7, xmm1
Run Code Online (Sandbox Code Playgroud)

这有什么实际的理由吗?性能提升?如果是,那么这下面是什么?或者它可能只是一种编码风格,有趣吗?

Pet*_*des 9

TL:DR:看起来可能是针对这个特定代码序列的一些微特定的调优.关于它的"通常建议"没有什么可以帮助其他情况.

在进一步考虑时,我认为@Iwillnotexist Idonotexist的理论最有可能:这是由一位认为这可能有所帮助的非专家撰写的.寄存器分配是一个很大的线索:通过选择低8中所有重复使用的寄存器,可以避免许多REX前缀.


XORPS在"浮动"域中运行,在某些Intel CPU(Nehalem和更高版本)上运行,而PXOR始终在"ivec"域中运行.

由于将每个ALU输出连接到每个ALU输入以直接转发结果将是昂贵的,因此CPU设计者将它们分解为域.(转发可以节省写回寄存器文件和重新读取的延迟).域跨越可能需要额外的1个周期的延迟(Intel SnB系列)或2个周期(Nehalem).

进一步阅读:我的回答是什么逻辑SSE内在函数之间有什么区别?


有两种理论出现在我面前:

  • 谁写了这个想法,PXOR和XORPS会提供更多的并行性,因为它们不会相互竞争.(这是错误的:PXOR可以在所有矢量ALU端口上运行,但XORPS不能).

  • 这是一些非常巧妙调整的代码,可以故意创建旁路延迟,以避免可能延迟执行下一个PCLMULQDQ的资源冲突.(或者如EOF所示,代码大小/对齐可能与它有关).

该代码的版权声明称"2011-2015英特尔",因此值得考虑它对某些最近的英特尔CPU有所帮助的可能性,而不仅仅是基于对英特尔CPU如何工作的误解.Nehalem是第一个包含PCLMULQDQ的CPU,这就是英特尔,所以如果有任何东西它将被调整为在AMD CPU上做得很糟糕.代码历史记录不在git repo中,只是添加了当前版本的5月6日提交.

英特尔白皮书(从2009年12月),它是基于使用PXOR只,而不是XORPS在其版本的2倍pclmul/2X XOR块.

Agner Fog的表格甚至没有显示Nehalem上PCLMULQDQ的数量,或者他们需要哪些端口.这是12c延迟,每8c吞吐量一个,所以它可能类似于Sandy/Ivybridge的18 uop实现.Haswell令人印象深刻的3 uops(2p0 p5),而在Broadwell(p0)和Skylake(p5)上只有1 uop.

XORPS只能在port5上运行(直到Skylake,它也可以在所有三个矢量ALU端口上运行).当其中一个输入来自PXOR时,Nehalem有2c旁路延迟.在SnB系列CPU上,Agner Fog说:

在某些情况下,使用错误类型的shuffle或布尔指令时没有旁路延迟.

因此我认为实际上没有额外的旁路延迟来从PXOR转发 - > Snor上的XORPS,因此唯一的影响是它只能在端口5 运行. 在Nehalem上,它实际上可能会延迟XORPS直到PSHUFB完成之后.

在主展开循环中,在XOR之后有一个PSHUFB,用于设置下一个PCLMUL的输入.SnB/IvB可以在p1/p5上运行整数shuffle(与Haswell不同,之后在p5上只有一个shuffle单元.但对于AVX2,它的宽度为256b).

由于竞争为下一个PCLMUL设置输入所需的端口似乎没有用,我最好的猜测是代码大小/对齐,如果在调整SnB时完成此更改.


在PCLMULQDQ超过4 uops的CPU上,它是微编码的.这意味着每个PCLMULQDQ都需要一个完整的uop缓存行.由于只有3个uop缓存行可以映射到x86指令的相同32B块,这意味着很多代码在SnB/IvB上根本不适合uop缓存.uop缓存的每一行只能缓存连续的指令.来自英特尔的优化手册:

一个方式中的所有微操作(uop缓存行)表示在代码中静态连续的指令,并且它们的EIP在相同的对齐的32字节区域内.

这听起来像是在循环中使用整数DIV非常相似的问题:在Intel SnB系列CPU上涉及微编码指令的循环的 分支对齐.通过正确的对齐,您可以使用uop缓存(英特尔性能计数器术语中的DSB).@Iwillnotexist Idonotexist对Haswell CPU的微编码指令进行了一些有用的测试,表明它们阻止了从环回缓冲区运行.(英特尔术语中的LSD).


在Haswell及更高版本中,PCLMULQDQ不是微编码的,因此它可以在其之前或之后与其他指令一起进入相同的uop缓存行.

对于以前的CPU,可能值得尝试调整代码以在较少的位置破坏uop缓存.OTOH,在uop缓存和传统解码器之间切换可能比总是从解码器运行更糟糕.

IDK如果这么大的展开真的很有用.它可能在SnB和Skylake之间变化很大,因为管道的微编码指令非常不同,SKL甚至可能不会影响PCLMUL的吞吐量.

  • 这段代码是为Westmere编写的,正如PDF本身所声称的那样.我碰巧认为这可能是博士生写的,而且他确实不知道自己在做什么.证据:1.随机使用`pxor/pxor`而不是`pxor/xorps`.2.没有使用`mov [au] ps`进行内存加载.3.可怕的regalloc,尤其是 `xmm10`,几乎所有insn大小增加1个字节.4.对于Westmere,`pclmulqdq`需要18 uop,最佳情况吞吐量是每8c 1,并且将w /前缀编码为7个字节,因此对齐和端口调度等微优化还为时过早.`pxor/xorps`这里是货物崇拜. (2认同)
  • @IwillnotexistIdonotexist:我的问题提供大多只是汇编/ x86/sse/avx /计算机架构/无锁/ stdatomic,所以我看到了所有这些并回答了好的问题.有很多人写出了很好的C/C++答案,而且我永远无法跟上这些标签中的问题量.(另外,当我查看微优化C/C++问题时,写一个答案通常需要很长时间,将asm与其他答案中的所有随机好的和坏的想法进行比较.所以我将自己局限于问题提要我可以跟上,因为我不能*不看*.) (2认同)
  • 虽然我开始训练自己只是从真正无聊的新手asm问题开始,问第一百个同一问题的一个小变化,以及16位DOS代码的愚蠢墙壁有5个不同的错误.令人难以置信的是,为什么人们认为在他们甚至没有使用调试器的情况下打扰他人的问题是可以的. (2认同)