我需要在 2021 年使用 _mm256_zeroupper 吗?

Ale*_*iev 2 c++ sse simd intrinsics avx

摘自Agner Fog 的“用 C++ 优化软件”

在某些 Intel 处理器上混合使用和不使用 AVX 支持编译的代码时会出现问题。由于 YMM 寄存器状态的变化,从 AVX 代码到非 AVX 代码会导致性能下降。在从 AVX 代码到非 AVX 代码的任何转换之前,应该通过调用内部函数 _mm256_zeroupper() 来避免这种惩罚。在以下情况下,这可能是必要的:

• 如果程序的一部分是使用 AVX 支持编译的,而程序的另一部分是在没有 AVX 支持的情况下编译的,则在离开 AVX 部分之前调用 _mm256_zeroupper()。

• 如果使用 CPU 调度在使用和不使用 AVX 的多个版本中编译函数,则在离开 AVX 部分之前调用 _mm256_zeroupper()。

• 如果一段使用AVX 支持编译的代码调用了编译器自带的库以外的库中的函数,而该库没有AVX 支持,则在调用库函数之前先调用_mm256_zeroupper()。

我想知道什么是英特尔处理器。具体来说,是否有过去五年制造的处理器。这样我就知道修复丢失的_mm256_zeroupper()电话是否为时已晚。

Pet*_*des 5

TL:DR:不要手动使用_mm256_zeroupper() 内在函数,编译器了解 SSE/AVX 转换内容并vzeroupper在需要的地方发出。(包括使用 YMM regs 自动矢量化或扩展 memcpy/memset/任何内容时。)


“一些英特尔处理器”是除了至强 Phi 之外的所有处理器。

Xeon Phi (KNL / KNM) 没有为运行传统 SSE 指令而优化的状态,因为它们纯粹是为运行 AVX-512 而设计的。旧版 SSE 指令可能总是将错误的依赖项合并到目标中。

在带有 AVX 或更高版本的主流 CPU 上,有两种不同的机制:保存脏鞋面(SnB 通过 Haswell 和 Ice Lake)或虚假依赖(Skylake)。请参阅为什么在 Skylake 上没有 VZEROUPPER 时此 SSE 代码会慢 6 倍?两种不同风格的 SSE/AVX 惩罚

关于 asm 效果的相关问答vzeroupper(在编译器生成的机器代码中):


C 或 C++ 源代码中的内部函数

您几乎不应该_mm256_zeroupper()在 C/C++ 源代码中使用。事情已经解决,让编译器vzeroupper在可能需要的地方自动插入一条指令,这几乎是编译器能够优化包含内在函数的函数并仍然可靠地避免转换惩罚的唯一明智方法。(特别是在考虑内联时)。所有主要的编译器都可以使用 YMM 寄存器自动矢量化和/或内联 memcpy/memset/array init,因此需要跟踪之后的使用vzeroupper情况。

约定是在调用或返回时让 CPU 处于清理状态,除非调用采用__m256/__m256i/d按值(在寄存器中或根本) args 的,或返回这样的值时。目标函数(被调用者或调用者)本质上必须是 AVX 感知的,并且需要一个脏上层状态,因为完整的 YMM 寄存器作为调用约定的一部分正在使用中。

x86-64 System V 在向量 regs 中传递向量。Windowsvectorcall也是如此,但最初的 Windows x64 约定(现在命名为“fastcall”以区别于“vectorcall”)通过隐藏指针在内存中按值传递向量。(这通过使每个 arg 始终适合 8 字节槽来优化可变参数函数。)IDK 编译 Windows 非向量调用调用的编译器如何处理这个问题,无论它们是否假设函数可能查看其 args 或至少仍然负责使用一个vzeroupper在即使它没有一些点。可能是的,但是如果您正在编写自己的代码生成后端或手写 asm,请查看您关心的某些编译器实际做了什么,如果这种情况与您相关。

一些编译器通过vzeroupper在从采用向量参数的函数返回之前也省略进行优化,因为显然调用者是 AVX 感知的。最重要的是,显然编译器不应该期望调用像这样的函数void foo(__m256i)会使 CPU 处于清理状态,所以被调用者仍然需要一个vzeroupperafter 这样的函数,之前call printf或其他。


编译器可以选择控制vzeroupper使用

例如, GCC -mno-vzeroupper/ clang -mllvm -x86-use-vzeroupper=0。(默认是-mvzeroupper执行上述行为,在可能需要时使用。)

这是-march=knl(Knight's Landing)暗示的,因为它在 Xeon Phi CPU 上不需要并且非常慢(因此应该主动避免)。

或者,如果您使用-mavx -mno-veroupper. glibc 为 strlen 等函数提供了一些手写的 asm,但其中大多数都有 AVX2 版本。因此,只要您不是在仅使用 AVX1 的 CPU 上,就可能根本不会使用传统 SSE 版本的字符串函数。

对于 MSVC,-arch:AVX在编译使用 AVX 内在函数的代码时,您绝对应该更喜欢使用。我认为某些版本的 MSVC 可能会生成代码,如果您混合使用__m128__m256不使用/arch:AVX. 但请注意,该选项甚至会使 128 位内在函数(例如_mm_add_ps使用 AVX 编码 ( vaddps) 而不是传统的 SSE ( addps) ),并且会让编译器使用 AVX 自动矢量化。

  • 当您在不同翻译单元之间调用自己的 AVX 向量化函数时,有充分的理由禁用“vzeroupper”的自动生成。如果函数不接受或返回向量,则编译器必须假设它需要旧版 SSE 状态并生成“vzeroupper”。在这种情况下,应该禁用自动“vzeroupper”,并在重要的地方为给定的 TU 手动插入内在函数。您可以将其保留为其他 TU 启用。 (2认同)

归档时间:

查看次数:

137 次

最近记录:

4 年,3 月 前