使用 AVX 指令和 -O3 编译选项得到错误的结果
Getting wrong results with using AVX instructions and -O3 compiling option
我用 AVX 指令编写了非常简单的程序,但是当我使用 g++ 编译器的 -O3 选项和 -O1 选项编译代码时,我得到了不同的结果,这是我的代码:
int main(int argc, char *argv[])
{
int d = 120;
__m256i r = _mm256_set1_epi32(d);
int * p = (int *) &r;
printf("r[0]: %d, ",p[0]);
printf("r[1]: %d, ",p[1]);
printf("r[2]: %d, ",p[2]);
printf("r[3]: %d, ",p[3]);
printf("r[4]: %d, ",p[4]);
printf("r[5]: %d, ",p[5]);
printf("r[6]: %d, ",p[6]);
printf("r[7]: %d \n",p[7]);
return 0;
}
这是我使用这些选项编译时的输出 (g++ test1.c -o test1 -m64 -O3 -ffast-math -march=native -mavx):
r[0]: 0, r[1]: 0, r[2]: 4195520, r[3]: 0, r[4]: -1880829792, r[5]: 32767, r[ 6]: 0, r[7]: 0
这是我使用这些选项编译时的输出 (g++ test1.c -o test1 -m64 -O1 -ffast-math -march=native -mavx):
r[0]: 120, r[1]: 120, r[2]: 120, r[3]: 120, r[4]: 120, r[5]: 120, r[6 ]: 120, r[7]: 120
第二个结果(-O1)是正确的,但是第一个是错误的。我不知道为什么会这样。
我刚刚看到你的评论说你已经解决了这个问题,但在搜索引擎上它仍然显示为 "no answer",这对有类似问题的人来说有点误导。原来这里的答案其实是错误的,但是楼主还没有把接受的答案改成正确的,所以我会更新这个。
简短的回答是,将 &r
转换为 (int*)
没有明确的行为。详情请参考 galinette 的回答。
定义的行为方式是将寄存器显式写入内存:
int[8] p;
_mm256_storeu_si128((__m256i*)p, r);
禁用严格别名会降低整个程序的性能!
将 &r
转换为 (int*)
没有定义的行为。 __m256i r
是 AVX 内部寄存器,不一定映射到内存。通过获取指向它的指针,您强制编译器将其写入内存,并且偶然它可能最终被映射到一个 int[8] 向量。
它可能适用于某些编译器、某些选项和某些情况。但是,您不应在代码中使用它,因为它可能会在没有警告的情况下停止工作。
"defined behavior"方法是:
int[8] p;
_mm256_storeu_si128((__m256i*)p, r);
printf("r[0]: %d, ",p[0]);
printf("r[1]: %d, ",p[1]);
printf("r[2]: %d, ",p[2]);
printf("r[3]: %d, ",p[3]);
printf("r[4]: %d, ",p[4]);
printf("r[5]: %d, ",p[5]);
printf("r[6]: %d, ",p[6]);
printf("r[7]: %d \n",p[7]);
然后你明确地将寄存器写入内存。这将做同样的事情,但无论编译器选项如何,它都将始终有效。由于禁用严格别名会降低整体代码优化,您的整个程序甚至会 运行 更快。
我用 AVX 指令编写了非常简单的程序,但是当我使用 g++ 编译器的 -O3 选项和 -O1 选项编译代码时,我得到了不同的结果,这是我的代码:
int main(int argc, char *argv[])
{
int d = 120;
__m256i r = _mm256_set1_epi32(d);
int * p = (int *) &r;
printf("r[0]: %d, ",p[0]);
printf("r[1]: %d, ",p[1]);
printf("r[2]: %d, ",p[2]);
printf("r[3]: %d, ",p[3]);
printf("r[4]: %d, ",p[4]);
printf("r[5]: %d, ",p[5]);
printf("r[6]: %d, ",p[6]);
printf("r[7]: %d \n",p[7]);
return 0;
}
这是我使用这些选项编译时的输出 (g++ test1.c -o test1 -m64 -O3 -ffast-math -march=native -mavx):
r[0]: 0, r[1]: 0, r[2]: 4195520, r[3]: 0, r[4]: -1880829792, r[5]: 32767, r[ 6]: 0, r[7]: 0
这是我使用这些选项编译时的输出 (g++ test1.c -o test1 -m64 -O1 -ffast-math -march=native -mavx):
r[0]: 120, r[1]: 120, r[2]: 120, r[3]: 120, r[4]: 120, r[5]: 120, r[6 ]: 120, r[7]: 120
第二个结果(-O1)是正确的,但是第一个是错误的。我不知道为什么会这样。
我刚刚看到你的评论说你已经解决了这个问题,但在搜索引擎上它仍然显示为 "no answer",这对有类似问题的人来说有点误导。原来这里的答案其实是错误的,但是楼主还没有把接受的答案改成正确的,所以我会更新这个。
简短的回答是,将 &r
转换为 (int*)
没有明确的行为。详情请参考 galinette 的回答。
定义的行为方式是将寄存器显式写入内存:
int[8] p;
_mm256_storeu_si128((__m256i*)p, r);
禁用严格别名会降低整个程序的性能!
将 &r
转换为 (int*)
没有定义的行为。 __m256i r
是 AVX 内部寄存器,不一定映射到内存。通过获取指向它的指针,您强制编译器将其写入内存,并且偶然它可能最终被映射到一个 int[8] 向量。
它可能适用于某些编译器、某些选项和某些情况。但是,您不应在代码中使用它,因为它可能会在没有警告的情况下停止工作。
"defined behavior"方法是:
int[8] p;
_mm256_storeu_si128((__m256i*)p, r);
printf("r[0]: %d, ",p[0]);
printf("r[1]: %d, ",p[1]);
printf("r[2]: %d, ",p[2]);
printf("r[3]: %d, ",p[3]);
printf("r[4]: %d, ",p[4]);
printf("r[5]: %d, ",p[5]);
printf("r[6]: %d, ",p[6]);
printf("r[7]: %d \n",p[7]);
然后你明确地将寄存器写入内存。这将做同样的事情,但无论编译器选项如何,它都将始终有效。由于禁用严格别名会降低整体代码优化,您的整个程序甚至会 运行 更快。