C 尝试专门使用内部函数时出现分段错误_mm256_storeu_pd()

C 尝试专门使用内部函数时出现分段错误_mm256_storeu_pd(),c,pointers,intrinsics,x86,C,Pointers,Intrinsics,X86,似乎是自己通过在mm256调用中键入cij2指针来修复的 so mm256 storeu pd((双*)cij2,vecC) 我不知道为什么这改变了什么 我正在编写一些代码,并试图利用Intel手动矢量化。但每当我运行代码时,我在尝试使用double*cij2时都会遇到分段错误 if( q == 0) { __m256d vecA; __m256d vecB; __m256d vecC; for (int i = 0; i < M; ++i) f

似乎是自己通过在mm256调用中键入cij2指针来修复的

so mm256 storeu pd((双*)cij2,vecC)

我不知道为什么这改变了什么

我正在编写一些代码,并试图利用Intel手动矢量化。但每当我运行代码时,我在尝试使用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;
      }
if(q==0)
{
__m256d-vecA;
__m256d-vecB;
__m256d-vecC;
对于(int i=0;i
我已经指出了cij2指针的问题。如果我注释掉包含该指针的两行代码,代码运行正常,它不会像应该的那样工作,但它实际上会运行

我的问题是,为什么会出现分段错误?我知道我已经正确分配了内存,内存是一个256个双精度向量,大小为64位

在阅读了评论之后,我来补充一些澄清。 我做的第一件事就是使用malloc将_-mm_-malloc更改为一个正常的分配。这两种方式都不会影响,但理论上会给我更多的喘息空间

第二个问题不是来自分配的空返回,我在数组中添加了几个循环以增加,并确保可以修改内存而不会崩溃,因此我相对确定这不是问题所在。问题似乎源于将数据从vecC加载到数组


最后,我不能使用BLAS调用。这是针对parallelisms类的。我知道用比我更智能的方式调用会简单得多,但不幸的是,如果我尝试这样做,我会得到一个0。

动态分配
double*cij2=(double*)malloc(4*sizeof(double))
但是你从来没有释放过它。这太傻了。使用
双cij2[4]
,尤其是如果你不想费心对齐它的话。你永远不需要一次多个暂存缓冲区,而且它是一个小的固定大小,所以只需要使用自动存储

在C++11中,您将使用
alignas(32)double cij2[4]
,这样您就可以使用
\u mm256\u store\u pd
而不是storeu(或者只是为了确保storeu不会因未对齐的地址而变慢)


如果你真的想调试你的原版,使用调试器在它出错时捕捉它,并查看指针值。确保它是合理的

你测试内存是否有效的方法(比如在内存上循环或注释内容)听起来可能会导致很多循环被优化掉,所以问题不会发生

当您的程序崩溃时,您也可以查看asm指令。向量内部函数相当直接地映射到x86 asm(除非编译器看到更有效的方法)


如果将水平和从k上的循环中提取出来,则实现将消耗更少。与其存储每个乘法结果并将其水平相加,不如使用向量加法到向量累加器中。hs在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
\uuuum256d cij\uvec=\umm256\usetzero\upd();
对于(int k=0;k
要获得良好的hsum256_pd实现(除了存储到内存和使用标量循环),请参阅(我在其中包括了一个AVX版本。将洗牌模式调整为256b双精度应该很容易。)这将对您的代码有很大帮助,因为您仍然有O(N^2)个水平和(而不是O(N^3)

理想情况下,您可以并行地累积4个
i
值的结果,而不需要水平求和

VADDPD的延迟为3到4个时钟,吞吐量为每1到0.5个时钟一个,因此您需要3到8个向量累加器来饱和执行单元。或者使用FMA,最多10个向量累加器(例如,在Haswell上,FMA…PD有5c延迟,每0.5c吞吐量一个)。请参阅以了解更多信息。还有标记wiki


此外,理想情况下,嵌套循环的方式是让您能够连续访问三个数组中的两个,因为缓存访问模式对于matmul(大量数据重用)至关重要。即使您不喜欢,也可以在适合缓存的时间转置小块。即使转置一个输入矩阵也可能是一个胜利,因为这需要O(N^2)并且加快了O(N^3)进程。我看到您的内部循环当前在访问
a[]
时有一个
lda
的跨步,我不确定
\u mm\u malloc()
,但是标准
malloc()
calloc()
在分配失败的情况下返回NULL。假设
\u mm\u malloc()
同样,您无法检查这种可能性。如果出现这种情况,您随后尝试使用指针时出现分段错误也就不足为奇了。另一方面,我观察到这似乎需要256位对齐的指针,但您的分配只能确保64位对齐。仅从文档判断,您似乎希望o使用
\u mm\u malloc(4*sizeof(double),256)
@JohnBollinger:storeu\u pd是未对齐的存储。store\u pd是对齐的存储。并且\u mm\u malloc通过