请考虑以下内容:
// foo.h
class Foo
{
public:
int x = 2;
int y = 3;
void DoSomething_SSE();
void DoSomething_AVX();
// ( Implicit default constructor is generated "inline" here )
};
// Foo_AVX.cpp, compiled with -mavx or /arch:AVX
void Foo::DoSomething_AVX()
{
// AVX optimised implementation here
}
// Foo_SSE.cpp, compiled with -msse2 or /arch:SSE2
void Foo::DoSomething_SSE()
{
// SSE optimised implementation here
}
这是问题所在:编译器将在每个翻译单元中生成具有“内联”语义的隐式默认构造函数(注意:内联语义不是不是意味着函数必须内联),并且-在没有内联构造函数的情况下-链接器将选择一个实现并放弃另一个实现。
如果链接器选择了在AVX编译单元中生成的构造函数,则该代码将在不支持AVX的计算机上以非法指令崩溃。
可以通过放入一个显式的默认构造函数来停止崩溃,该构造函数可以是__forceinline(以确保每个编译单元内联一次),也可以在标头中声明并在编译单元中定义,该编译单元以最低的公共分母指令集。
但是,肯定有一种方法可以使语言更好地处理这种情况,而不必编写虚拟函数。?
(在Mac OS X上为llvm-clang ++ 9.x.x / x64)
答案 0 :(得分:2)
使用gcc或clang -mavx -fno-implement-inlines
编译AVX转换单元;如果函数不能简单地内联,则链接器必须从SSE转换单元中找到符号。
来自GCC手册:
-fno-implement-inlines
为了节省空间,请勿发出由#pragma implementation
控制的内联函数的脱机副本。如果这会导致链接器错误 这些函数并非在调用它们的地方都内联。
Clang也支持此选项。
这不会不会禁止任何内容的内联,只会禁止发出声明为inline
或在类定义中的函数的独立定义。
启用优化后,问题中的小型默认构造函数应内联(并使用当前函数/编译单元的目标ISA选项),这在大多数情况下都无关紧要。但这将确保未优化的构建在非AVX机器上正常运行。
答案 1 :(得分:1)
似乎另一个选择是不使用编译器标志设置指令集-将其保留为默认值,并仅包装需要增强指令集的功能:
#include Foo.h
// Switch on AVX optimisations for the function where they're needed
#pragma clang attribute push (__attribute__((target("arch=sandybridge"))), apply_to = function)
void Foo::DoSomething_AVX()
{
// AVX optimised implementation here
}
#pragma clang attribute pop
使用#pragma clang属性push(...)比简单的[[]]
或__attribute__(())
有点冗长,但似乎具有将属性自动应用于任何模板的优点在编译指示的范围内实例化的代码等。
答案 2 :(得分:0)
将实现放入一个单独的.cpp文件中,一切正常。 另一种方法是使那些函数/方法/构造函数内联。 第三种方式(取决于编译器)是将它们设置为“弱引用”属性。