尝试使用内在函数时出现分段错误_mm256_storeu_pd()

时间:2016-09-15 16:55:58

标签: c pointers intrinsics

似乎已经通过在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。

1 个答案:

答案 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以了解详情。还有标签wiki。

此外,理想情况下,您可以通过一种方式嵌套循环,使您可以连续访问三个阵列中的两个阵列,因为缓存访问模式对于matmul(大量数据重用)至关重要。即使你不喜欢并且在适合缓存的时候移植小块。即使转换一个输入矩阵也可以获胜,因为这会花费O(N ^ 2)并加速O(N ^ 3)过程。我在访问lda时看到您的内部循环目前有A[]的步幅。