用于AVX和SSE的visual studio的cpu调度程序

时间:2013-03-14 10:27:31

标签: c++ visual-studio sse avx

我使用两台电脑。一个没有AVX支持,另一个没有AVX。让我的代码在运行时找到我的CPU支持的指令集并选择适当的代码路径会很方便。 我遵循Agner Fog的建议来制作一个CPU调度员(http://www.agner.org/optimize/#vectorclass)。但是,在我的机器上进行AVX编译并与visual studio链接时,启用AVX的代码会导致代码在运行时崩溃。

我的意思是例如我有两个源文件,其中一个SSE2指令集定义了一些SSE2指令,另一个源文件定义了AVX指令集和一些AVX指令。在我的主函数中,如果我只引用SSE2函数,代码仍会因启用了AVX和AVX指令的任何源代码而崩溃。我可以解决这个问题的任何线索吗?

编辑: 好的,我想我已经解决了这个问题。我正在使用Agner Fog的矢量类,我已经定义了三个源文件:

//file sse2.cpp - compiled with /arch:SSE2
#include "vectorclass.h"
float func_sse2(const float* a) {
    Vec8f v1 = Vec8f().load(a);
    float sum = horizontal_add(v1);
    return sum;
}
//file avx.cpp - compiled with /arch:AVX
#include "vectorclass.h"
float func_avx(const float* a) {
    Vec8f v1 = Vec8f().load(a);
    float sum = horizontal_add(v1);
    return sum;
}
//file foo.cpp - compiled with /arch:SSE2
#include <stdio.h>
extern float func_sse2(const float* a);
extern float func_avx(const float* a);
int main() {
    float (*fp)(const float*a); 
    float a[] = {1,2,3,4,5,6,7,8};
    int iset = 6;
    if(iset>=7) { 
        fp = func_avx;  
    }
    else { 
        fp = func_sse2;
    }
    float sum = (*fp)(a);
    printf("sum %f\n", sum);
}

这次崩溃。如果我改为在func_SSE2中使用Vec4f,它不会崩溃。我不明白这一点。只要我没有AVX的另一个源文件,我可以单独使用Vec8f和SSE2。 Agner Fog的手册说

“使用256位浮点矢量类没有优势(Vec8f, Vec4d)除非指定了AVX指令集,否则使用起来会很方便 无论如何,如果使用和不使用AVX使用相同的源代码,这些类。 编译时,每个256位向量将简单地分成两个128位向量 没有AVX。“

但是,当我有两个使用Vec8f的源文件,一个用SSE2编译,一个用AVX编译,然后我就崩溃了。

EDIT2: 我可以从命令行开始工作

>cl -c sse2.cpp
>cl -c /arch:AVX avx.cpp
>cl foo.cpp sse2.obj avx.obj
>foo.exe

EDIT3: 但是,这会崩溃

>cl -c sse2.cpp
>cl -c /arch:AVX avx.cpp
>cl foo.cpp avx.obj sse2.obj
>foo.exe

另一个线索。显然,关联的顺序很重要。如果avx.obj在sse2.obj之前崩溃,但如果sse2.obj在avx.obj之前,它就不会崩溃。我不确定它是否选择了正确的代码路径(我现在无法访问我的AVX系统),但至少它不会崩溃。

3 个答案:

答案 0 :(得分:7)

我意识到这是一个古老的问题,那个问过它的人似乎已经不在了,但我昨天遇到了同样的问题。这就是我的成果。

编译时,sse2.cpp和avx.cpp文件都会生成不仅包含函数的对象文件,还包含任何必需的模板函数。 (例如Vec8f::load)这些模板函数也使用请求的指令集进行编译。

这意味着你的sse2.obj和avx.obj目标文件都将包含Vec8f::load的定义,每个定义使用相应的指令集编译。

但是,由于编译器将Vec8f::load视为外部可见,因此将其置于“COMDAT”状态。带有&#39; selectany&#39;的目标文件的一部分(又名&#39;选择任何&#39;)标签。这告诉链接器如果它看到该符号的多个定义,例如在2个不同的目标文件中,则允许它选择它喜欢的任何一个。 (这样做是为了减少最终可执行文件中的重复代码,否则会因模板和内联函数的多个定义而导致大小膨胀。)

您遇到的问题与此直接相关,因为传递给链接器的目标文件的顺序会影响它选择的对象。具体来说,它似乎是在选择它看到的第一个定义。

如果这是avx.obj,那么将始终使用AVX编译版本的Vec8F::load。这将在不支持该指令集的机器上崩溃。 另一方面,如果sse2.obj是第一个,那么将始终使用SSE2编译版本。这不会崩溃,但即使支持AVX,它也只会使用SSE2指令。

如果你看一下链接器&#39; map&#39;就是这种情况。文件输出(使用/ map选项生成。)以下是相关(已编辑)的摘录 -

//
// link with sse2.obj before avx.obj
//
0001:00000080  _main                             foo.obj
0001:00000330  func_sse2@@YAMPBM@Z               sse2.obj
0001:00000420  ??0Vec256fe@@QAE@XZ               sse2.obj
0001:00000440  ??0Vec4f@@QAE@ABT__m128@@@Z       sse2.obj
0001:00000470  ??0Vec8f@@QAE@XZ                  sse2.obj <-- sse2 version used
0001:00000490  ??BVec4f@@QBE?AT__m128@@XZ        sse2.obj
0001:000004c0  ?get_high@Vec8f@@QBE?AVVec4f@@XZ  sse2.obj
0001:000004f0  ?get_low@Vec8f@@QBE?AVVec4f@@XZ   sse2.obj
0001:00000520  ?load@Vec8f@@QAEAAV1@PBM@Z        sse2.obj <-- sse2 version used
0001:00000680  ?func_avx@@YAMPBM@Z               avx.obj
0001:00000740  ??BVec8f@@QBE?AT__m256@@XZ        avx.obj

//
// link with avx.obj before sse2.obj
//
0001:00000080  _main                             foo.obj
0001:00000270  ?func_avx@@YAMPBM@Z               avx.obj
0001:00000330  ??0Vec8f@@QAE@XZ                  avx.obj <-- avx version used
0001:00000350  ??BVec8f@@QBE?AT__m256@@XZ        avx.obj
0001:00000380  ?load@Vec8f@@QAEAAV1@PBM@Z        avx.obj <-- avx version used
0001:00000580  ?func_sse2@@YAMPBM@Z              sse2.obj
0001:00000670  ??0Vec256fe@@QAE@XZ               sse2.obj
0001:00000690  ??0Vec4f@@QAE@ABT__m128@@@Z       sse2.obj
0001:000006c0  ??BVec4f@@QBE?AT__m128@@XZ        sse2.obj
0001:000006f0  ?get_high@Vec8f@@QBE?AVVec4f@@XZ  sse2.obj
0001:00000720  ?get_low@Vec8f@@QBE?AVVec4f@@XZ   sse2.obj

至于修复它,那是另一回事。在这种情况下,通过强制avx版本具有其自己的不同命名的模板函数版本,以下钝器应该起作用。这将增加生成的可执行文件大小,因为即使sse2和avx版本相同,它也将包含相同功能的多个版本。

// avx.cpp
namespace AVXWrapper {
\#include "vectorclass.h"
}
using namespace AVXWrapper;

float func_avx(const float* a)
{
    ...
}

但是有一些重要的限制 - (a)如果所包含的文件管理任何形式的全球国家,它将不再是真正的全球性,因为你将拥有2&#39;半全球状态&#39;版本,和 (b)您无法将vectorclass变量作为avx.cpp中定义的其他代码和函数之间的参数传递。

答案 1 :(得分:2)

链接顺序很重要的事实使我认为obj文件中可能存在某种初始化代码。如果初始化代码是公共的,则只采用第一个。我无法重现它,但你应该能够在汇编列表中看到它(用/ c /Ftestavx.asm编译)

答案 2 :(得分:1)

将SSE和AVX函数放在不同的CPP文件中,并确保编译SSE版本,而不是/arch:AVX