似乎已经通过在mm256调用中输入cij2指针的类型来自行修复了
所以_mm256_storeu_pd((double *)cij2,vecC);
我不知道为什么会改变任何东西......
我正在编写一些代码并试图利用英特尔手动矢量化。但是每当我运行代码时,我都会在尝试使用double * cij2时出现分段错误。
if( q == 0)
{
__m256d vecA;
__m256d vecB;
__m256d vecC;
for (int i = 0; i < M; ++i)
for (int j = 0; j < N; ++j)
{
double cij = C[i+j*lda];
double *cij2 = (double *)malloc(4*sizeof(double));
for (int k = 0; k < K; k+=4)
{
vecA = _mm256_load_pd(&A[i+k*lda]);
vecB = _mm256_load_pd(&B[k+j*lda]);
vecC = _mm256_mul_pd(vecA,vecB);
_mm256_storeu_pd(cij2, vecC);
for (int x = 0; x < 4; x++)
{
cij += cij2[x];
}
}
C[i+j*lda] = cij;
}
我已经将问题指向了cij2指针。如果我注释掉包含该指针的2行代码运行正常,它就不会像它应该的那样工作但它实际上会运行。
我的问题是为什么我会在这里遇到分段错误?我知道我已经正确分配了内存,并且内存是一个256位的双倍矢量,大小为64位。
阅读评论后,我来补充一些说明。 我做的第一件事是使用malloc将_mm_malloc更改为正常分配。不应该影响任何一种方式,但理论上会给我一些更多的喘息空间。
第二个问题不是来自分配的空返回,我添加了几个循环来增加数组,并确保我可以修改内存而不会崩溃所以我相对肯定不是问题。问题似乎源于从vecC到阵列的数据加载。
最后我不能使用BLAS电话。这是一个并行类。我知道要比我更聪明地打电话要简单得多,但不幸的是,如果我尝试的话,我会得到0。
答案 0 :(得分:0)
您动态分配double *cij2 = (double *)malloc(4*sizeof(double));
,但您永远不会释放它。这太傻了。使用double cij2[4]
,特别是如果您不打算将其对齐。您一次不需要多个暂存缓冲区,并且它只是一个小的固定大小,所以只需使用自动存储。
在C ++ 11中,您使用alignas(32) double cij2[4]
,因此您可以使用_mm256_store_pd
代替storeu。 (或者只是为了确保商店不会因未对齐的地址而变慢)。
如果您确实想要调试原始文件,请使用调试器在segfaults时捕获它,并查看指针值。确保它是明智的。
你测试内存有效的方法(比如循环播放或者评论出来的东西)听起来好像会导致很多循环被优化掉,所以问题不会发生。
当程序崩溃时,您还可以查看asm说明。矢量内在函数直接映射到x86 asm(除非编译器看到更有效的方式)。
如果你将循环中的水平和拉出k ,那么你的实现会减少很多。不是存储每个乘法结果并水平添加它,而是使用向量累加器中的向量加法。 hsum它在循环之外k。
__m256d cij_vec = _mm256_setzero_pd();
for (int k = 0; k < K; k+=4) {
vecA = _mm256_load_pd(&A[i+k*lda]);
vecB = _mm256_load_pd(&B[k+j*lda]);
vecC = _mm256_mul_pd(vecA,vecB);
cij_vec = _mm256_add_pd(cij_vec, vecC); // TODO: use multiple accumulators to keep multiple VADDPD or VFMAPD instructions in flight.
}
C[i+j*lda] = hsum256_pd(cij_vec); // put the horizontal sum in an inline function
对于好的hsum256_pd实现(除了存储到内存和使用标量循环之外),请参阅Fastest way to do horizontal float vector sum on x86(我在那里包含了AVX版本。应该很容易将混洗模式调整为256b双精度。 )这将帮助您的代码 lot ,因为您仍然有O(N ^ 2)个水平和(但不是O(N ^ 3)这个变化)。
理想情况下,您可以并行累积4个i
值的结果,而不需要水平求和。
VADDPD的延迟为3到4个时钟,每1到0.5个时钟的吞吐量为1,因此需要3到8个向量累加器来使执行单元饱和。或者使用FMA,最多10个向量累加器(例如,在Haswell,其中FMA ... PD具有5c延迟,每0.5c吞吐量一个)。请参阅Agner Fog's instruction tables and optimization guides以了解详情。还有x86标签wiki。
此外,理想情况下,您可以通过一种方式嵌套循环,使您可以连续访问三个阵列中的两个阵列,因为缓存访问模式对于matmul(大量数据重用)至关重要。即使你不喜欢并且在适合缓存的时候移植小块。即使转换一个输入矩阵也可以获胜,因为这会花费O(N ^ 2)并加速O(N ^ 3)过程。我在访问lda
时看到您的内部循环目前有A[]
的步幅。