zin*_*nga 3 x86 assembly sse avx micro-optimization
从我能收集到的所有信息来看,混合 SSE 和 128 位 (E)VEX 编码指令没有性能损失。这表明将两者混合应该没问题。当 SSE 指令通常比 VEX 等效指令短 1 个字节时,这可能是有益的。
但是,我从未见过任何人或任何编译器这样做。作为一个例子,在Intel的AVX(128位)MD5实现方式中,各种vmovdqa可替换为movaps(或者这vshufps可以用较短的替换shufps,由于DEST和SRC1寄存器是相同的)。
是否有任何特殊原因可以避免 SSE,或者我遗漏了什么?
你是对的,如果 YMMvzeroupper上限从 a 中已知为零,那么混合 AVX128 和 SSE 没有损失,并且在节省代码大小时不这样做是一个错过的优化。
另请注意,如果您不需要 REX 前缀,它只会节省代码大小。2 字节 VEX 相当于 SSE1 的 REX + 0F。编译器确实尝试支持低寄存器以希望避免 REX 前缀,但我认为他们没有考虑在每条指令中使用哪些寄存器组合来最小化 REX 前缀总数。(或者,如果他们确实尝试这样做,他们并不擅长)。人类可以花时间做这样的计划。
大多数时候它很小,只是偶尔的代码大小字节。这通常是一件好事,可以帮助前端。(或保存了UOPblendvps xmm, xmm, <XMM0>在pblendvps xmm, xmm, xmm, xmm英特尔的CPU(同样为Pd和pblendvb),如果你可以安排使用它,而无需其他movaps)
如果你弄错了,不利的一面是 SSE/AVX 转换惩罚(在 Haswell 和 Ice Lake 上),或者对 Skylake 的错误依赖。 为什么这个 SSE 代码在 Skylake 上没有 VZEROUPPER 的情况下会慢 6 倍?. IDK,如果 Zen2 这样做的话;Zen1 将 256 位操作拆分为 2 个 uop,并且不关心 vzeroupper。
为了让编译器安全地执行此操作,他们必须跟踪更多内容以确保在 YMM 寄存器具有脏的上半部分时不会在函数内运行 SSE 指令。编译器无法将 AVX 代码生成限制为仅 128 位指令,因此他们必须开始跟踪可能弄脏 YMM 上半部分的执行路径。
不过,我认为他们必须这样做反正全功能的基础上,知道何时使用vzeroupper之前ret(在不接受或返回功能__m256/i/d的价值,这将调用者已经使用宽矢量意思)。
但是不需要vzeroupper与是否movaps性能安全是一回事,因此以类似的方式进行跟踪将是另一回事。找到避免使用 VEX 前缀是安全的所有情况。
不过,可能在某些情况下很容易证明它是安全的。如果编译器使用保守算法,当分支可能会或可能不会弄脏上限时,该算法会遗漏一些优化,并且在这种情况下始终使用 VEX,并且始终使用vzeroupper.