Sim*_*n F 2 c gcc sse intrinsics
背景:我开发了一个用C/C++编写的计算密集型工具,它必须能够在各种不同的x86_64处理器上运行.为了加速浮点和整数的计算,代码包含了很多SSE*内在函数,它们具有针对不同CPU SSE功能定制的不同路径.(由于在程序开始时检测到CPU标志并用于设置布尔值,我假设定制的代码块的分支预测将非常有效地工作).
为简单起见,我假设只需要考虑SSE2到SSE4.2.
为了访问4.2路径的SSE4.2内在函数fpr,我需要使用gcc的-msse4.2选项.
这个问题 的问题我遇到的是,至少在6.1.0,GCC去并实现了SSE2内在的,mm_cvtsi32_si128,与SSE4.2指令,pinsrd.
如果我使用-msse2限制编译,它将使用sse2指令,movd,即.英特尔"内在指南"说它应该使用的那个.
这有点令人讨厌.
1)关键问题是当程序在pre4.2 CPU上运行时,程序现在会因非法指令而崩溃.我无法控制使用什么硬件,因此可执行文件需要与旧机器兼容,但需要利用新硬件上的功能.
2)根据英特尔内在指南,pinrd指令比它取代的mov慢得多.(pinsrd更通用但不需要这样).
有谁知道如何使gcc 只使用内在指南所说的应该使用的指令,但仍然允许在同一个编译单元中通过SSE4*访问所有SSE2?
更新:我还应该注意,在Linux,Windows和OSX下使用各种不同的编译器编译相同的代码,因此如果可能的话,宁愿避免或至少拥有最少的编译器特定扩展.
Update2 :(感谢@PeterCordes)似乎如果启用了优化,gcc将在适当的时候恢复使用来自pinsrd的movd.
如果-msse4.2
在编译步骤中将标志赋予gcc的命令行,它将假定它可以自由地使用最多整个翻译单元的SSE 4.2指令集.这可能会导致您描述的行为.如果您需要仅使用SSE2及以下代码的代码,则需要使用-msse2
(或者如果您正在为x86_64构建,则根本不使用标志).
我能想到的一些选择是:
如果您可以在功能级别轻松分解代码,那么gcc的多版本化功能可以提供帮助.它需要一个相对较新版本的编译器,但它允许你做这样的事情(取自上面的链接):
__attribute__ ((target ("default")))
int foo ()
{
// The default version of foo.
return 0;
}
__attribute__ ((target ("sse4.2")))
int foo ()
{
// foo version for SSE4.2
return 1;
}
__attribute__ ((target ("arch=atom")))
int foo ()
{
// foo version for the Intel ATOM processor
return 2;
}
__attribute__ ((target ("arch=amdfam10")))
int foo ()
{
// foo version for the AMD Family 0x10 processors.
return 3;
}
int main ()
{
int (*p)() = &foo;
assert ((*p) () == foo ());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,gcc将foo()
根据CPU的功能自动编译运行时的不同版本并分派到相应的版本.
您可以将不同的实现(SSE2,SSE4.2等)分解为不同的转换单元,然后在运行时适当地分配给正确的实现.
您可以将所有SIMD代码放入共享库,并使用不同的编译器标志多次构建共享库.然后在运行时,您可以检测CPU的功能并加载适当版本的共享库.这是像英特尔数学核心库这样的库所采用的方法.
归档时间: |
|
查看次数: |
413 次 |
最近记录: |