【问题标题】:AVX equivalent for _mm_movelh_ps_mm_movelh_ps 的 AVX 等效项
【发布时间】:2019-11-20 12:51:34
【问题描述】:

因为没有_mm_movelh_ps 的AVX 版本,我通常使用_mm256_shuffle_ps(a, b, 0x44) 作为AVX 寄存器的替代品。但是,我记得在其他问题中阅读过,如果可能的话,应该首选没有控制整数的 swizzle 指令(如_mm256_unpacklo_ps_mm_movelh_ps)(出于某种我不知道的原因)。昨天,我突然想到,另一种选择可能是使用以下内容:

_mm256_castpd_ps(_mm256_unpacklo_pd(_mm256_castps_pd(a), _mm256_castps_pd(b)));

由于演员表应该是无操作的,这是否比使用 _mm256_shuffle_ps 在性能方面更好\相等\更差?

另外,如果确实如此,如果有人能用简单的语言解释(我对汇编和微体系结构的理解非常有限)为什么应该更喜欢没有控制整数的指令,那就太好了。

提前致谢

补充说明: Clang 实际上将 shuffle 优化为 vunpcklpd: https://godbolt.org/z/9XFP8D 所以看来我的想法还不错。但是,GCC 和 ICC 会创建 shuffle 指令。

【问题讨论】:

  • 如果可能的话,应该首选[没有立即控制轮空的洗牌]听起来像我写的东西:P
  • @PeterCordes:感谢您的回答(如下)。我觉得我以后应该直接向你提出问题。 :D
  • 嘿,如果它对未来的读者有用,我可能仍然会回答它。顺便说一句,请注意我在您接受后所做的更新。
  • @PeterCordes 看到了更新。

标签: c++ sse intrinsics avx


【解决方案1】:

避免立即数可节省 1 个字节的机器码大小;就这样。出于性能考虑,它位于列表的底部,但由于这个原因,所有其他相同的随机播放(如 _mm256_unpacklo_pd)都比直接控制字节略好。

(但是将控制操作数放在另一个向量中,例如 vpermilps can 或 vpermd requires 通常会更糟,除非您在长时间运行的循环中有一些奇怪的前端瓶颈,并且可以在循环。不是很合理,此时您必须在 asm 中手动编写才能非常关心代码大小/对齐;在 C++ 中,这仍然不是您可以真正直接控制的东西。)

由于演员表应该是无操作的,这是否比使用 _mm256_shuffle_ps 在性能方面更好\相等\更差?

Ice Lake 有 2/clock vshufps vs. 1/clock vunpcklpd,根据uops.info 在真实硬件上的测试,在端口 1 或端口 5 上运行。绝对使用_mm256_shuffle_ps。微不足道的额外代码大小成本可能实际上对早期 CPU 没有任何影响,并且对于 ICL 的未来利益而言可能是值得的,除非您确定端口 5 不会成为瓶颈。

Ice Lake 在端口 1 上有一个第二个 shuffle 单元,可以处理一些常见的 XMM 和通道内 YMM shuffle,包括 vpshufb 和一些 2-input shuffle,例如 vshufps。我不知道为什么它不使用该控制向量将vunpcklpd 解码为vshufps,或者设法在端口1 上运行该随机播放。我们知道随机播放硬件本身可以进行随机播放,所以我猜它只是设置隐式随机播放的控制硬件问题,以某种方式将操作码映射到随机播放控件。

除此之外,它在较旧的 AVX CPU 上是相同或更好的;没有 CPU 会因为在其他 PS 指令之间使用 PD shuffle 而受到惩罚。任何现有 CPU 的唯一不同是代码大小。像 K8 和 Core 2 这样的旧 CPU 的 pd shuffle 比 ps 更快,但是没有 AVX 的 CPU 具有具有这种弱点的 shuffle 单元。此外,AVX 非破坏性指令在哪个操作数必须是目标之间存在级别差异。


正如您从 Godbolt 链接中看到的,在随机播放之前/之后有零个额外指令。 “cast”内在函数没有进行转换,只是重新解释以保持 C++ 类型系统满意,因为英特尔决定为 __m256__m256d(与 __m256i)提供不同的类型,而不是拥有一个通用 YMM类型。不过,他们选择不像 ARM 那样使用单独的 uint8x16uint32x4 向量;对于整数 SIMD,只需 __m256i

所以编译器不需要为强制转换发出额外的指令,在实践中确实如此;他们不会引入额外的vmovaps/apd 注册副本或类似的东西。


如果您使用 clang,您可以方便地编写它,然后让 clang 的 shuffle 优化器为您发出 vunpcklpd。或者在其他情况下,无论如何都要做任何事情;有时它会做出比来源更糟糕的选择,但通常它做得很好。

Clang 使用 -march=icelake-client 出错了,即使你写了 _mm256_shuffle_ps,仍然使用 vunpcklpd。 (或者根据周围的代码,可能会将洗牌优化为其他东西的一部分。)

Related bug report.

【讨论】:

  • 有趣的是,即使对 -march=icelake-client,Clang 也做了这种糟糕的优化。你认为应该提交一份错误报告以引起他们的注意吗?
  • 是的,应该报告。至少应该将其更改为更喜欢 vshufps 用于 icelake,可能也用于通用。
  • 好的,我会处理这个问题(今天或明天)并在问题中发布链接。
  • 对此答案的建议编辑或评论将是 LLVM 错误报告链接的更好位置。感谢您花时间这样做。顺便说一句,您可能希望在您的 LLVM 错过优化错误中链接这个答案,或者至少是 uops.info/… uops.info 搜索结果。
  • @chtz:我在后面的回答中确实提到了这一点;当您使用 64 位块时,Core 2 和 K8 具有更快的随机播放。 Conroe/Merom 是第一代 65nm Core 2,这就是我的意思; Penryn 将 shuffle ALU 从 64 位扩大到 128 位,所以 pshufb 并且一切都很快,而不仅仅是 pshuflwshufpd。在Fastest way to do horizontal float vector sum on x86 中提供更具体的详细信息。所以是的,这可能与没有 AVX 的 __m128i 有关。顺便说一句,KNL 对vpshufb 和其他字节/字元素混洗的吞吐量很差,但对于 32 位和 64 位块来说很好。
猜你喜欢
  • 1970-01-01
  • 2013-06-10
  • 2018-01-04
  • 1970-01-01
  • 2011-08-19
  • 1970-01-01
  • 2020-02-09
  • 1970-01-01
  • 2020-10-05
相关资源
最近更新 更多