我使用两台电脑.一个没有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);
}
Run Code Online (Sandbox Code Playgroud)
这崩溃了.如果我改为在func_SSE2中使用Vec4f,它不会崩溃.我不明白这一点.只要我没有AVX的另一个源文件,我可以单独使用Vec8f和SSE2.Agner Fog的手册说
"除非指定AVX指令集,否则使用256位浮点矢量类(Vec8f,Vec4d)没有任何优势,但如果使用和不使用AVX使用相同的源代码,则无论如何都可以方便地使用这些类.在没有AVX的情况下编译时,每个256位向量将简单地分成两个128位向量."
但是,当我有两个使用Vec8f的源文件,一个用SSE2编译,一个用AVX编译,然后我崩溃了.
Edit2:我可以从命令行开始工作
>cl -c sse2.cpp
>cl -c /arch:AVX avx.cpp
>cl foo.cpp sse2.obj avx.obj
>foo.exe
Run Code Online (Sandbox Code Playgroud)
编辑3:但是,这会崩溃
>cl -c sse2.cpp
>cl -c /arch:AVX avx.cpp
>cl foo.cpp avx.obj sse2.obj
>foo.exe
Run Code Online (Sandbox Code Playgroud)
另一个线索.显然,关联的顺序很重要.如果avx.obj在sse2.obj之前崩溃,但如果sse2.obj在avx.obj之前,它就不会崩溃.我不确定它是否选择了正确的代码路径(我现在无法访问我的AVX系统),但至少它不会崩溃.
我意识到这是一个古老的问题,那个问它的人似乎已经不在了,但我昨天遇到了同样的问题.这就是我的成果.
编译时,sse2.cpp和avx.cpp文件都会生成不仅包含函数的对象文件,还包含任何必需的模板函数.(例如Vec8f::load)这些模板函数也使用请求的指令集进行编译.
这意味着您的sse2.obj和avx.obj目标文件都将包含Vec8f::load使用相应指令集编译的每个文件的定义.
但是,由于编译器视为Vec8f::load外部可见,因此它将对象文件的"COMDAT"部分与"selectany"(也称为"pick any")标签放在一起.这告诉链接器如果它看到该符号的多个定义,例如在2个不同的目标文件中,则允许它选择它喜欢的任何一个.(这样做是为了减少最终可执行文件中的重复代码,否则将通过模板和内联函数的多个定义来扩大其大小.)
您遇到的问题与此直接相关,因为传递给链接器的目标文件的顺序正在影响它选择的目标文件.具体来说,它似乎是在选择它看到的第一个定义.
如果这是avx.obj Vec8F::load则将始终使用AVX编译版本.这将在不支持该指令集的计算机上崩溃.另一方面,如果sse2.obj是第一个,那么将始终使用SSE2编译版本.这不会崩溃,但即使支持AVX,它也只会使用SSE2指令.
如果您查看链接器"map"文件输出(使用/ 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
Run Code Online (Sandbox Code Playgroud)
至于修理它,那是另一回事.在这种情况下,通过强制avx版本具有其自己的不同命名版本的模板函数,以下钝器应该起作用.这将增加生成的可执行文件大小,因为即使sse2和avx版本相同,它也将包含相同功能的多个版本.
// avx.cpp
namespace AVXWrapper {
\#include "vectorclass.h"
}
using namespace AVXWrapper;
float func_avx(const float* a)
{
...
}
Run Code Online (Sandbox Code Playgroud)
但是有一些重要的限制 - (a)如果所包含的文件管理任何形式的全球状态,它将不再是真正的全球性,因为你将拥有2个"半全球"版本,并且(b)你将无法将vectorclass变量作为avx.cpp中定义的其他代码和函数之间的参数传递.
| 归档时间: |
|
| 查看次数: |
3301 次 |
| 最近记录: |