我明白了,那些人很好地混合了pxor和xorps指令,就像下面的片段一样:
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
这有什么实际的理由吗?性能提升?如果是,那么这下面是什么?或者它可能只是一种编码风格,有趣吗?
TL:DR:看起来可能是针对这个特定代码序列的一些微特定的调优.关于它的“通常建议”没有什么可以帮助其他情况.在进一步考虑时,我认为@Iwillnotexist Idonotexist的理论最有可能:这是由一位认为这可能有所帮助的非专家撰写的.寄存器分配是一个很大的线索:通过选择低8中的所有重复使用的寄存器,可以避免许多REX前缀.
XORPS在“浮动”域中运行,在某些Intel CPU(Nehalem和更高版本)上运行,而PXOR始终在“ivec”域中运行.
由于将每个ALU输出连接到每个ALU输入以直接转发结果将是昂贵的,因此CPU设计者将它们分解为域. (转发可以节省写回寄存器文件和重新读取的延迟).域跨越可能需要额外的1个周期的延迟(Intel SnB系列)或2个周期(Nehalem).
进一步阅读:我在What’s the difference between logical SSE intrinsics?的回答
有两种理论出现在我面前:
>无论谁写下这个想法,PXOR和XORPS都会提供更多的并行性,因为它们不会相互竞争. (这是错误的:PXOR可以在所有矢量ALU端口上运行,但XORPS不能).
>这是一些非常巧妙调整的代码,可以故意创建旁路延迟,以避免可能延迟执行下一个PCLMULQDQ的资源冲突. (或者如EOF所示,代码大小/对齐可能与它有关).
该代码的版权声明称“2011-2015英特尔”,因此值得考虑它对某些最近的英特尔CPU有所帮助的可能性,而不仅仅是基于对英特尔CPU如何工作的误解. Nehalem是第一个包含PCLMULQDQ的CPU,这就是英特尔,所以如果有任何东西它将被调整为在AMD CPU上做得很糟糕.代码历史记录不在git repo中,只是添加了当前版本的5月6日提交.
Intel whitepaper (from Dec 2009)仅基于使用的PXOR,而不是XORPS,在其2x 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说:
In some cases, there is no bypass delay
when using the wrong type of shuffle or Boolean instruction.
所以我认为从PXOR转发实际上没有额外的旁路延迟 – >在SnB上进行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缓存的每一行只能缓存连续的指令.来自英特尔的优化手册:
All micro-ops in a Way (uop cache line) represent instructions which are statically contiguous in the code and have their EIPs within the same aligned 32-byte region.
这听起来像是在循环中使用整数DIV非常相似的问题:
0700. @Iwillnotexist Idonotexist对Haswell CPU的微编码指令进行了一些有用的测试,表明它们阻止了从环回缓冲区运行. (英特尔术语中的LSD).
在Haswell及更高版本中,PCLMULQDQ不是微编码的,因此它可以在其之前或之后与其他指令一起进入相同的uop缓存行.
对于以前的CPU,可能值得尝试调整代码以在较少的位置破坏uop缓存. OTOH,在uop缓存和传统解码器之间切换可能比总是从解码器运行更糟糕.
IDK如果这么大的展开真的很有用.它可能在SnB和Skylake之间变化很大,因为管道的微编码指令非常不同,SKL甚至可能不会影响PCLMUL的吞吐量.