在GCC中使用不同版本的SSE内置函数的正确方法是什么?

15

我将通过举例来提出我的问题。现在我有一个名为 do_something()的函数。

它有三个版本: do_something() do_something_sse3() do_something_sse4()。当我的程序运行时,会检测CPU功能(查看是否支持SSE3或SSE4),并相应地调用其中一个版本。

问题是:当我使用GCC构建我的程序时,我必须为 do_something_sse4()设置 -msse4才能进行编译(例如,头文件<smmintrin.h>将被包含)。

然而,如果我设置了 -msse4,则GCC将可以使用SSE4指令,并且一些 do_something_sse3()中的内部函数也会被翻译成一些SSE4指令。因此,如果我的程序在只支持SSE3(但不支持SSE4)的CPU上运行,则在调用 do_something_sse3()时会引发“非法指令”的错误。

也许我有一些不好的实践。你能给出一些建议吗?谢谢。


6
我认为标准的方法是将不同版本编译在单独的编译单元中。 - Mysticial
3
@BoPersson,一些函数可以通过使用新的SSE4指令进一步加速。由于我们正在处理视频编解码,这可能非常耗时,因此我认为SSE4优化是有意义的。 - shengbinmeng
@BoPersson:诚然,对于大多数工作负载来说,SSE4是一种有点傻的ISA扩展(虽然round[ss/sd/ps/pd]偶尔很棒,而ptestblendps肯定有它们的用途)。但是,如果正确使用,SSSE3(主要是pshufbpmulhrsw)以及AVX可以产生巨大的差异。 - Stephen Canon
3
仍有许多计算机不支持SSE4/SSE3,甚至没有任何SSE支持。非SSE版本适用于它们。 - shengbinmeng
@BoPersson:通常,非SSE版本会慢3~4倍。是的,没有人想要使用它,但有时他们不得不使用(例如,使用完全不支持SSE的机器)。 - shengbinmeng
显示剩余5条评论
4个回答

11

我认为Mystical的建议很好,但如果你真的想在一个文件中完成它,你可以使用合适的编译指示,例如:

#pragma GCC target("sse4.1")

需要GCC 4.4,AFAIR。


谢谢您的建议。我稍后也会尝试 #pragma 指令。 - shengbinmeng
即使使用#pragma GCC target("sse4"),也无法包含smmintrin.h。 - Trass3r

2
我认为你想构建一个所谓的“CPU调度器”。我已经为GCC制作了一个(据我所知),但尚未使其与Visual Studio配合使用。
cpu dispatcher for visual studio for AVX and SSE 我建议查看Agner Fog的vectorclass和dispatch_example.cpp文件。http://www.agner.org/optimize/#vectorclass
g++ -O3 -msse2   -c dispatch_example.cpp -od2.o
g++ -O3 -msse4.1 -c dispatch_example.cpp -od5.o
g++ -O3 -mavx    -c dispatch_example.cpp -od8.o
g++ -O3 -msse2      instrset_detect.cpp d2.o d5.o d8.o

0

这里有一个编译每个优化设置的单独对象文件的示例: http://notabs.org/lfsr/software/index.htm

但是,即使使用gcc链接时优化(-flto),这种方法也会失败。那么如何为不同处理器构建具有完全优化的单个可执行文件呢?我能找到的唯一解决方案是使用包含指令使C文件表现为单个编译单元,以便不需要-flto。以下是使用该方法的示例: http://notabs.org/blcutil/index.htm


0
如果您在i686或x86_64机器上使用GCC 4.9或更高版本,则应该能够使用内置函数,而不管您的-march=XXX-mXXX选项如何。您可以相应地编写do_something()
void do_something()
{
    byte temp[18];

    if (HasSSE2())
    {
        const __m128i i = _mm_loadu_si128((const __m128i*)(ptr));
        ...
    }
    else if (HasSSSE3())
    {
        const __m128i MASK = _mm_set_epi8(12,13,14,15, 8,9,10,11, 4,5,6,7, 0,1,2,3);
        _mm_storeu_si128(reinterpret_cast<__m128i*>(temp),
           _mm_shuffle_epi8(_mm_loadu_si128((const __m128i*)(ptr)), MASK));
    }
    else
    {
        // Do the byte swap/endian reversal manually
        ...
    }
}

你必须提供HasSSE2()HasSSSE3()等函数。另请参见类似于CPUID的信息的内部函数

还请参见GCC问题57202 - 请使内部函数头文件(如immintrin.h)可在没有编译器标志的情况下使用。但我不认为该功能有效。我经常遇到编译失败的情况,因为GCC没有提供内部函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接