我写了一些代码,该代码在当前CPU上可用时使用AVX Intrinsics。在GCC和Clang中,与Visual C 不同,为了使用内在,您必须在命令行上启用它们。
GCC和Clang的问题是,当您启用这些选项时,您将为编译器免费统治使用源文件中各地的这些说明。当您拥有包含内联函数或模板函数的标头文件时,这是非常糟糕的,因为编译器将使用AVX指令生成这些功能。
链接时,重复的功能将被丢弃。但是,由于某些源文件是用-mavx
编译的,而有些则不是,因此内联/模板功能的各种汇编将有所不同。如果您不幸,链接器将随机选择具有AVX说明的版本,从而导致该程序在没有AVX的系统上运行时崩溃。
GCC用#pragma GCC target
解决此问题。您可以关闭标头文件的特殊说明,并且生成的代码不会使用AVX:
#pragma GCC push_options
#pragma GCC target("no-avx")
#include "MyHeader.h"
#pragma GCC pop_options
Clang有这样的东西吗?它似乎忽略了这些选项并无论如何都会生成AVX代码。
等效于GCC push_options / GCC target / GCC pop_options
的叮当声是clang attribute push / clang attribute pop
Pragmas,以及target
属性:
#pragma clang attribute push (__attribute__((target("pclmul,sse4.1,ssse3"))), apply_to=function)
// ...
#pragma clang attribute pop
这是:
等效的#pragma GCC push_options
#pragma GCC target("pclmul", "sse4.1", "ssse3")
// ...
#pragma GCC pop_options
请注意,在GCC target
pragma获取目标选项的逗号列表的地方,Clang target
属性会在内部进行单个字符串,以逗号限制在内部。
clang支持负目标选项(例如"no-avx"
),但我更喜欢使用正面选项添加到由命令行选项选择的功能集中。
您可能应该使用 static inline
而不是 inline
,因此只有从该翻译单元中的呼叫者使用-mavx
编译的函数的版本。
链接器仍将合并实际的重复项,而不仅仅是按名称选择一个非内部定义。
这也具有一个优势,即编译器不会浪费时间发出独立的定义,以确定其决定介入该翻译单元中的每个呼叫者。
如果您习惯了GCC/CLANG的方式,并为其设计代码是有道理的。并请注意,如果您要编译使用AVX的功能,则需要启用MSVC。否则,它将混合vex和非vex编码,导致大规模惩罚,而不是在 _mm256_add_ps
环的末端使用vex编码为128位_mm_add_ps
之类的东西。
因此,您基本上遇到了MSVC相同的问题,编译_mm_whatever
将制作仅AVX的机器代码。