Gcc 使用SSE/AVX INTRINISCS时的架构效应

Gcc 使用SSE/AVX INTRINISCS时的架构效应,gcc,compilation,sse,avx,icc,Gcc,Compilation,Sse,Avx,Icc,我想知道编译器是如何处理内部函数的 #pragma intel optimization_parameter target_arch=AVX 如果使用SSE2内部函数使用include和compile with-mavx标志。编译器将生成什么?它会生成AVX或SSE代码吗 如果使用AVX2内部函数,则使用include和compile with-msse2标志。编译器将生成什么?它只生成SSE还是AVX代码 编译器如何处理内部函数? 如果使用intrinsic,它是否有助于编译器理解循环中的依

我想知道编译器是如何处理内部函数的

#pragma intel optimization_parameter target_arch=AVX
如果使用SSE2内部函数使用include和compile with-mavx标志。编译器将生成什么?它会生成AVX或SSE代码吗

如果使用AVX2内部函数,则使用include和compile with-msse2标志。编译器将生成什么?它只生成SSE还是AVX代码

编译器如何处理内部函数? 如果使用intrinsic,它是否有助于编译器理解循环中的依赖关系,从而实现更好的矢量化

例如,这里发生了什么——或者? 查看所有3个窗格

上下文 我试图用不同的CPU特性SSE4和AVX2构建相同功能的不同版本。 我正在用SSE Intrinsics编写同一个版本,用AVX Intrinsics编写一个版本。 假设他们的名字是MyFunSSE和MyFunAVX。两者都在同一个文件中

如何使编译器的相同方法适用于MSVC、GCC和ICC,并仅使用各自的函数构建它们?

GCC和clang要求启用所有使用的扩展。否则这是一个编译时错误,如错误:调用always_inline'\uuuuM256d\uMM256\uMask\uLoadu\uPd\uuuuM256d,\uuuuMMask8,const void*'时内联失败:特定于目标的选项不匹配

使用-march=haswell或任何比启用特定扩展更可取的方法,因为这还设置了适当的调优选项。您不会忘记像-mpopcnt这样有用的函数,它可以让std::bitset::count内联popcnt指令,并使BMI2 shlx/shrx 1 uop与3相比,所有变量计数移位都更有效

MSVC和ICC不会,并且将允许您使用内部函数来发出它们无法自动矢量化的指令

如果您使用AVX intrinsics,您肯定应该启用AVX。我想我已经读到/看到了,如果没有这些,MSVC不会总是在它应该使用的地方使用vzeroupper

对于支持GNU扩展GCC、clang、ICC的编译器,可以在编译单元中的特定函数上使用_attribute__targetavx之类的东西。或者更好的方法是,使用属性\uuuu targetrach=haswell也可以设置调整选项。但这也会启用AVX2和FMA,这可能是您不想要的。我不确定是否可以设置目标属性-mtune=xx

而且

__属性__目标将阻止它们内联到具有其他目标选项的函数中,因此,如果函数本身太小,请小心在它们将内联到的函数中使用此选项

另见 用于在相同函数名的多个定义上使用不同的目标选项,用于编译器支持的运行时调度。但我不认为有一种可移植到MSVC的方法可以做到这一点

对于MSVC,您不需要任何东西,尽管正如我所说,我认为使用AVX intrinsic而不使用-arch:AVX通常是个坏主意,因此您最好将它们放在单独的文件中。但是对于AVX和AVX2+FMA,或者SSE2和SSE4.2,没有任何东西都可以

只需将AVX2_函数定义为空字符串,而不是_属性_targetavx2,fma

e、 g

使用GCC和clang,宏将扩展为_属性_目标内容;对于MSVC和ICC,情况并非如此

国际商会布拉格语: 记录一个pragma,您希望将其放在AVX函数之前,以确保在使用_mm256intrinsic的函数中正确使用vzeropper

#pragma intel optimization_parameter target_arch=AVX
对于ICC,您可以将TARGET_AVX定义为这样,并始终在函数前面的一行中单独使用它,在该行中可以放置_属性或pragma。如果ICC不希望在声明中使用宏,您可能还需要单独的宏来定义和声明函数。和一个宏来结束一个AVX函数块,如果你想在它们后面有非AVX函数。对于非ICC编译器,这将是空的。

GCC和clang要求您启用所有使用的扩展。否则这是一个编译时错误,如错误:调用always_inline'\uuuuM256d\uMM256\uMask\uLoadu\uPd\uuuuM256d,\uuuuMMask8,const void*'时内联失败:特定于目标的选项不匹配

使用-march=haswell或任何比启用特定扩展更可取的方法,因为这还设置了适当的调优选项。您不会忘记像-mpopcnt这样有用的函数,它可以让std::bitset::count内联popcnt指令,并使BMI2 shlx/shrx 1 uop与3相比,所有变量计数移位都更有效

MSVC和ICC不会,并且将允许您使用内部函数来发出它们无法自动矢量化的指令

如果您使用AVX intrinsics,您肯定应该启用AVX。我想我已经读到/看到了,如果没有这些,MSVC不会总是在它应该使用的地方使用vzeroupper

对于支持GNU扩展GCC、clang、ICC的编译器,可以在编译单元中的特定函数上使用_attribute__targetavx之类的东西。或者更好的方法是,使用属性\uuuu targetrach=haswell也可以设置调整选项。但这也会启用AVX2和FMA,这可能是您不想要的。我不确定是否可以设置目标属性-mtune=xx

而且

__属性__目标将阻止它们内联到具有其他目标选项的函数中,因此,如果函数本身太小,请小心在它们将内联到的函数中使用此选项

另见 用于在相同函数名的多个定义上使用不同的目标选项,用于编译器支持的运行时调度。但我不认为有一种可移植到MSVC的方法可以做到这一点

对于MSVC,您不需要任何东西,尽管正如我所说,我认为使用AVX intrinsic而不使用-arch:AVX通常是个坏主意,因此您最好将它们放在单独的文件中。但是对于AVX和AVX2+FMA,或者SSE2和SSE4.2,没有任何东西都可以

只需将AVX2_函数定义为空字符串,而不是_属性_targetavx2,fma

e、 g

使用GCC和clang,宏将扩展为_属性_目标内容;对于MSVC和ICC,情况并非如此

国际商会布拉格语: 记录一个pragma,您希望将其放在AVX函数之前,以确保在使用_mm256intrinsic的函数中正确使用vzeropper

#pragma intel optimization_parameter target_arch=AVX
对于ICC,您可以将TARGET_AVX定义为这样,并始终在函数前面的一行中单独使用它,在该行中可以放置_属性或pragma。如果ICC不希望在声明中使用宏,您可能还需要单独的宏来定义和声明函数。和一个宏来结束一个AVX函数块,如果你想在它们后面有非AVX函数。对于非ICC编译器,这将是空的。

如果在启用-mavx2的情况下编译代码,编译器通常会生成所谓的VEX编码指令。在_mm_loadu_ps的情况下,这将生成vmovups而不是movups,这几乎是等效的,只是后者将只修改目标寄存器的较低128位,而前者将清除较低128位以上的所有内容。但是,它只能在至少支持AVX的计算机上运行

对于其他指令,如AVX,还有允许三个操作数的额外优势,即目标可以不同于两个源,在某些情况下可以避免复制寄存器。例如:

_mm_mul_ps(_mm_add_ps(a,b), _mm_sub_ps(a,b));
为SSE编译时需要寄存器副本movaps,但为AVX编译时不需要:

关于使用AVX内部函数但不使用AVX进行编译,编译器要么像gcc/clang一样失败,要么以静默方式生成相应的指令,然后在不支持AVX的机器上失败。有关详细信息,请参阅@PeterCordes answer

附录:如果您想在编译时根据体系结构实现不同的功能,可以使用ifdef\uuuu AVX\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

我认为在同一个编译单元中实现它们是困难的。最简单的解决方案是构建不同的共享库或甚至不同的二进制文件,并使用一个小型二进制文件来检测可用的CPU功能并加载相应的库/二进制文件。我假设在这个主题上有相关的问题。

如果您在编译代码时启用了-mavx2,那么编译器通常会生成所谓的VEX编码指令。在_mm_loadu_ps的情况下,这将生成vmovups而不是movups,这几乎是等效的,只是后者将只修改目标寄存器的较低128位,而前者将清除较低128位以上的所有内容。但是,它只能在至少支持AVX的计算机上运行

对于其他指令,如AVX,还有允许三个操作数的额外优势,即目标可以不同于两个源,在某些情况下可以避免复制寄存器。例如:

_mm_mul_ps(_mm_add_ps(a,b), _mm_sub_ps(a,b));
为SSE编译时需要寄存器副本movaps,但为AVX编译时不需要:

关于使用AVX内部函数但不使用AVX进行编译,编译器要么像gcc/clang一样失败,要么以静默方式生成相应的指令,然后在不支持AVX的机器上失败。有关详细信息,请参阅@PeterCordes answer

附录:如果您想在编译时根据体系结构实现不同的功能,可以使用ifdef\uuuu AVX\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu


我认为在同一个编译单元中实现它们是困难的。最简单的解决方案是构建不同的共享库或甚至不同的二进制文件,并使用一个小型二进制文件来检测可用的CPU功能并加载相应的库/二进制文件。我假设在这个话题上有相关的问题。

这些问题来自与国际商会的问题。我使用SSE内在函数编写函数,并使用/arch:SSE2和/Qax=CORE-AVX2编译它们,但得到的代码在仅SSE的CPU上失败。我不明白…@Royi:那么你应该为它创建一个包含错误指令的调试器,并询问它!您的Godbolt链接没有使用我能看到的任何非SSE2指令,除了在使用-mavx2的选项卡中。问题是代码太大,我无法访问非AVX CPU。我只是看到了我编译的代码
虽然我使用了ICC的这个功能,但在使用非AVX CPU的人身上失败。我将尝试编辑这个问题以使我的目标更清晰。目标是能够使用SSE和AVX路径生成代码。但如果我在编译时构建一个函数,其中SSE intrinsic是一种风格,AVX是另一种风格,那么我必须启用AVX,然后SSE代码也会生成AVX,我被卡住了。Peter,我添加了一些关于上下文的信息。这些问题来自ICC的问题。我使用SSE内在函数编写函数,并使用/arch:SSE2和/Qax=CORE-AVX2编译它们,但得到的代码在仅SSE的CPU上失败。我不明白…@Royi:那么你应该为它创建一个包含错误指令的调试器,并询问它!您的Godbolt链接没有使用我能看到的任何非SSE2指令,除了在使用-mavx2的选项卡中。问题是代码太大,我无法访问非AVX CPU。我只是看到我编译的代码在非AVX CPU的人身上失败了,尽管我使用了ICC的这个特性。我会尝试编辑这个问题,让我的目标更清晰。目标是能够使用SSE和AVX路径生成代码。但是如果我在编译时构建一个函数,其中SSE intrinsic是一种风格,AVX是另一种风格,那么我必须启用AVX,然后SSE代码也会生成AVX,我被卡住了。Peter,我添加了一些关于上下文的信息。感谢您添加的信息。请看一下我问题中上下文中添加的信息。谢谢您添加的信息。看看我的问题中添加的信息。更新了我的答案。我想你只是在寻找GNU C的属性,我在这些链接中寻找什么?编译器在使用-mavx2编译时使用VEX编码,而在不使用时则不使用。这就是gcc/clang/ICC的工作方式。还有MSVC for-arch:AVX或not.BTW,如果你也要包含catch-all-include,那么包含它是没有意义的。始终只包含,除非您想在MSVC上包含较少的内容,以防止意外使用某些扩展,因为它的目标选项模型不同于gcc/clang.Updated my answer。我想你只是在寻找GNU C的属性,我在这些链接中寻找什么?编译器在使用-mavx2编译时使用VEX编码,而在不使用时则不使用。这就是gcc/clang/ICC的工作方式。还有MSVC for-arch:AVX或not.BTW,如果你也要包含catch-all-include,那么包含它是没有意义的。始终只包含,除非您希望在MSVC上包含较少的内容,以防止意外使用某些扩展,因为它的目标选项模型不同于gcc/clang。