使用AVX指令和-O3编译选项得到错误的结果

4

我使用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)是正确的,但第一个结果却是错误的。我不知道为什么会出现这种情况。

4
在黑暗中戳:-fno-strict-aliasing 是否能解决问题?你的代码违反了 strict-aliasing 规则,但这不应该成为问题,因为 SSE/AVX 类型已声明为 may_alias - Mysticial
没有使用clang进行复现。你使用的是哪个版本的g++? - user58697
1
我无法重现您的问题(gcc 4.9.2)。 - edmz
谢谢大家... -fno-strict-aliasing 解决了我的问题。 - user3687068
@user3687068 -- 将其发布为自我回答,我会点赞 :)(如果您无法,请告诉我,我会将其设为社区wiki) - LThode
1
@Mysticial:我不这么认为,我找不到一个版本的avxintrin.h,其中__m256i会被may_alias定义为typedef - user3079266
2个回答

6

禁用严格别名规则将降低整个程序的性能!

&r强制转换为(int*)没有定义的行为。 __m256i r是一个AVX寄存器内在函数,并不一定映射到内存。通过获取指针,强制编译器将它写入内存,但有时可能会以int[8]向量的方式映射。

这种方法在一些编译器、一些选项和一些情况下可能有效。但是,由于没有警告,您不应在代码中使用此方法。

"定义行为"的方法是:

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]); 

然后您将寄存器显式写入内存。这样做会产生相同的效果,但不管编译器选项如何,都能正常工作。由于禁用严格别名会降低整个代码优化,因此您的整个程序甚至会运行得更快。


1
我刚刚看到你的评论,说你已经解决了问题,但在搜索引擎上仍然显示为“无答案”,这对于有类似问题的人有点误导。原来的答案实际上是错误的,但原帖作者还没有将被接受的答案改为正确的答案,所以我会更新这个答案。
简短的回答是,将&r转换为(int*)没有定义的行为。请参考galinette的答案获取更多详细信息。
进行此操作的定义行为方式是显式地将寄存器写入内存:
int[8] p;
_mm256_storeu_si128((__m256i*)p, r);

1
“多个指针指向内存中的同一位置”并不是程序违反严格别名规则时被错误编译的描述。任何对实际规则感兴趣的人都应该阅读C11标准中的6.5:6和6.5:7。 - Pascal Cuoq

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