use*_*931 6 sse simd avx visual-c++ auto-vectorization
假设我有一个用c ++编写的函数,它在很多向量上执行矩阵向量乘法.它需要一个指向要转换的向量数组的指针.我是否正确假设编译器无法有效地优化SIMD指令,因为它在编译时不知道传递指针的对齐(SSE需要16字节对齐或AVX需要32字节对齐)?或者数据的内存对齐与最佳SIMD代码无关,数据对齐只会影响缓存性能?
如果对齐对于生成的代码很重要,我怎么能让(visual c ++)编译器知道我打算只传递具有特定对齐值的函数?
理论上,自Nehalem以来,对齐英特尔处理器并不重要.因此,您的编译器应该能够生成代码,其中指针对齐或不对齐不是问题.
自Nehalem以来,未对齐的加载/存储指令在英特尔处理器上具有相同的性能.然而,在AVX与Sandy Bridge一起到达之前,未对准的负载无法与另一个微操作融合操作折叠.
此外,即使在AVX之前,为了避免高速缓存行分割具有16字节对齐的内存的惩罚仍然有用,因此编译器添加代码直到指针是16字节对齐仍然是合理的.
由于AVX不再使用对齐的加载/存储指令,因此编译器没有理由添加代码以使指针对齐16字节或32字节..
但是,有理由使用对齐的内存来避免使用AVX进行缓存行分割.因此,编译器添加代码以使指针32字节对齐是合理的,即使它仍使用未对齐的加载指令.
所以在实践中,一些编译器在被告知假设指针对齐时会生成更简单的代码.
我不知道告诉MSVC指针是否对齐的方法.使用GCC和Clang(自3.6起),您可以使用内置的__builtin_assume_aligned.有了ICC和GCC,你可以使用#pragma omp simd aligned.使用ICC,您也可以使用__assume_aligned.
例如,GCC编译这个简单的循环
void foo(float * __restrict a, float * __restrict b, int n)
{
//a = (float*)__builtin_assume_aligned (a, 16);
//b = (float*)__builtin_assume_aligned (b, 16);
for(int i=0; i<(n & (-4)); i++) {
b[i] = 3.14159f*a[i];
}
}
Run Code Online (Sandbox Code Playgroud)
用gcc -O3 -march=nehalem -S test.c,然后wc test.s使160行.然而,如果使用__builtin_assume_aligned然后wc test.s只给出45行.当我在两种情况下都这样做时,clang返回110行.
因此,在clang通知编译器时,阵列对齐没有区别(在这种情况下),但与GCC一致.计算代码行不是衡量性能的充分指标,但我不打算在这里发布所有程序集我只想说明编译器在告知数组对齐时可能产生非常不同的代码.
当然,GCC没有假设阵列对齐的额外开销可能在实践中没有区别.你必须测试和看到.
无论如何,如果你想从SIMD中获得最大的收益,我不会依赖编译器来正确地完成它(特别是对于MSVC).你的例子matrix*vector通常很差(但可能不适用于某些特殊情况),因为它的内存带宽受限.但是如果你选择matrix*matrix没有编译器就可以在没有很多不符合C++标准的帮助的情况下进行优化.在这些情况下,您将需要内在函数/内置函数/汇编程序,您可以在其中明确控制对齐.
编辑:
GCC的程序集包含许多无关的行,这些行不属于文本段.执行gcc -O3 -march=nehalem -S test.c然后使用objdump -d和计算文本(代码)段中的108行给出不使用行而__builtin_assume_aligned仅使用16行.这更清楚地表明GCC在假设阵列对齐时产生非常不同的代码.
编辑:
我继续foo在MSVC 2013中测试上面的功能.它产生未对齐的负载,代码比GCC短得多(我只在这里显示主循环):
$LL3@foo:
movsxd rax, r9d
vmulps xmm1, xmm0, XMMWORD PTR [r10+rax*4]
vmovups XMMWORD PTR [r11+rax*4], xmm1
lea eax, DWORD PTR [r9+4]
add r9d, 8
movsxd rcx, eax
vmulps xmm1, xmm0, XMMWORD PTR [r10+rcx*4]
vmovups XMMWORD PTR [r11+rcx*4], xmm1
cmp r9d, edx
jl SHORT $LL3@foo
Run Code Online (Sandbox Code Playgroud)
自Nehalem(2008年末)以来,处理器应该没问题.但MSVC仍然拥有不是四的倍数的数组的清理代码,即使我告诉编译器它是四((n & (-4))的倍数.至少GCC做对了.
由于AVX可以折叠未打开的负载,我用AVX检查GCC以查看代码是否相同.
void foo(float * __restrict a, float * __restrict b, int n)
{
//a = (float*)__builtin_assume_aligned (a, 32);
//b = (float*)__builtin_assume_aligned (b, 32);
for(int i=0; i<(n & (-8)); i++) {
b[i] = 3.14159f*a[i];
}
}
Run Code Online (Sandbox Code Playgroud)
如果没有__builtin_assume_alignedGCC生产168条装配线,它只生产17条生产线.
| 归档时间: |
|
| 查看次数: |
1506 次 |
| 最近记录: |