【发布时间】:2015-07-14 10:55:05
【问题描述】:
最近我正在通过有限差分法研究计算电动力学的数值求解器。
求解器实现起来非常简单,但很难达到现代处理器的理论吞吐量,因为对加载的数据只有 1 次数学运算,例如:
#pragma ivdep
for(int ii=0;ii<Large_Number;ii++)
{ Z[ii] = C1*Z[ii] + C2*D[ii];}
Large_Number 约为 1,000,000,但不大于 10,000,000
我曾尝试手动展开循环并编写 AVX 代码,但未能使其更快:
int Vec_Size = 8;
int Unroll_Num = 6;
int remainder = Large_Number%(Vec_Size*Unroll_Num);
int iter = Large_Number/(Vec_Size*Unroll_Num);
int addr_incr = Vec_Size*Unroll_Num;
__m256 AVX_Div1, AVX_Div2, AVX_Div3, AVX_Div4, AVX_Div5, AVX_Div6;
__m256 AVX_Z1, AVX_Z2, AVX_Z3, AVX_Z4, AVX_Z5, AVX_Z6;
__m256 AVX_Zb = _mm256_set1_ps(Zb);
__m256 AVX_Za = _mm256_set1_ps(Za);
for(int it=0;it<iter;it++)
{
int addr = addr + addr_incr;
AVX_Div1 = _mm256_loadu_ps(&Div1[addr]);
AVX_Z1 = _mm256_loadu_ps(&Z[addr]);
AVX_Z1 = _mm256_add_ps(_mm256_mul_ps(AVX_Zb,AVX_Div1),_mm256_mul_ps(AVX_Za,AVX_Z1));
_mm256_storeu_ps(&Z[addr],AVX_Z1);
AVX_Div2 = _mm256_loadu_ps(&Div1[addr+8]);
AVX_Z2 = _mm256_loadu_ps(&Z[addr+8]);
AVX_Z2 = _mm256_add_ps(_mm256_mul_ps(AVX_Zb,AVX_Div2),_mm256_mul_ps(AVX_Za,AVX_Z2));
_mm256_storeu_ps(&Z[addr+8],AVX_Z2);
AVX_Div3 = _mm256_loadu_ps(&Div1[addr+16]);
AVX_Z3 = _mm256_loadu_ps(&Z[addr+16]);
AVX_Z3 = _mm256_add_ps(_mm256_mul_ps(AVX_Zb,AVX_Div3),_mm256_mul_ps(AVX_Za,AVX_Z3));
_mm256_storeu_ps(&Z[addr+16],AVX_Z3);
AVX_Div4 = _mm256_loadu_ps(&Div1[addr+24]);
AVX_Z4 = _mm256_loadu_ps(&Z[addr+24]);
AVX_Z4 = _mm256_add_ps(_mm256_mul_ps(AVX_Zb,AVX_Div4),_mm256_mul_ps(AVX_Za,AVX_Z4));
_mm256_storeu_ps(&Z[addr+24],AVX_Z4);
AVX_Div5 = _mm256_loadu_ps(&Div1[addr+32]);
AVX_Z5 = _mm256_loadu_ps(&Z[addr+32]);
AVX_Z5 = _mm256_add_ps(_mm256_mul_ps(AVX_Zb,AVX_Div5),_mm256_mul_ps(AVX_Za,AVX_Z5));
_mm256_storeu_ps(&Z[addr+32],AVX_Z5);
AVX_Div6 = _mm256_loadu_ps(&Div1[addr+40]);
AVX_Z6 = _mm256_loadu_ps(&Z[addr+40]);
AVX_Z6 = _mm256_add_ps(_mm256_mul_ps(AVX_Zb,AVX_Div6),_mm256_mul_ps(AVX_Za,AVX_Z6));
_mm256_storeu_ps(&Z[addr+40],AVX_Z6);
}
上面的 AVX 循环实际上比 Inter 编译器生成的代码要慢一点。
编译器生成的代码可以达到大约 8G flops/s,大约是 3GHz Ivybridge 处理器单线程理论吞吐量的 25%。我想知道是否有可能达到这样的简单循环的吞吐量。
谢谢!
【问题讨论】:
-
如果您的编译器支持,请在所有指针上尝试编译器特定的
restrict关键字。如果不是,请在 C99 模式下编写和编译,并使用标准的restrict关键字。此外,对齐所有指针,并告诉编译器它们是对齐的。另外,忘记余数,而是要求填充输入。 -
感谢您的 cmets,所有数组都使用 _mm_malloc(size,64) 与 64 字节对齐,并且使用 #pragma ivdep 而不是限制来向量化 for 循环。
-
你使用的是什么编译器和操作系统?
-
我看到一大堆未对齐的加载和存储(
_mm256_loadu_ps、_mm256_storeu_ps)。是否可以不对齐数据以支持用对齐的版本(_mm256_load_ps、_mm256_store_ps)替换这些内在函数?
标签: c++ optimization simd intrinsics avx