Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 矩阵优化-使用内部函数和循环展开时的分段错误_C_Segmentation Fault_Matrix Multiplication_Intrinsics_Loop Unrolling - Fatal编程技术网

C 矩阵优化-使用内部函数和循环展开时的分段错误

C 矩阵优化-使用内部函数和循环展开时的分段错误,c,segmentation-fault,matrix-multiplication,intrinsics,loop-unrolling,C,Segmentation Fault,Matrix Multiplication,Intrinsics,Loop Unrolling,我目前正在尝试使用内部函数和循环展开优化矩阵运算。有一个分割错误,我想不出来。以下是我更改的代码: const int UNROLL = 4; void outer_product(matrix *vec1, matrix *vec2, matrix *dst) { assert(vec1->dim.cols == 1 && vec2->dim.cols == 1 && vec1->dim.rows == dst->dim.row

我目前正在尝试使用内部函数和循环展开优化矩阵运算。有一个分割错误,我想不出来。以下是我更改的代码:

const int UNROLL = 4;

void outer_product(matrix *vec1, matrix *vec2, matrix *dst) {
    assert(vec1->dim.cols == 1 && vec2->dim.cols == 1 && vec1->dim.rows == dst->dim.rows && vec2->dim.rows == dst->dim.cols);
    __m256 tmp[4];
    for (int x = 0; x < UNROLL; x++) {
        tmp[x] = _mm256_setzero_ps();
    } 
    for (int i = 0; i < vec1->dim.rows; i+=UNROLL*8) {
        for (int j = 0; j < vec2->dim.rows; j++) {     
            __m256 row2 = _mm256_broadcast_ss(&vec2->data[j][0]);
            for (int x = 0; x<UNROLL; x++) {
                tmp[x] = _mm256_mul_ps(_mm256_load_ps(&vec1->data[i+x*8][0]), row2); 
                _mm256_store_ps(&dst->data[i+x*8][j], tmp[x]);
            } 
        }
    }
}

void matrix_multiply(matrix *mat1, matrix *mat2, matrix *dst) {
    assert (mat1->dim.cols == mat2->dim.rows && dst->dim.rows == mat1->dim.rows && dst->dim.cols == mat2->dim.cols);
    for (int i = 0; i < mat1->dim.rows; i+=UNROLL*8) {
        for (int j = 0; j < mat2->dim.cols; j++) {
        __m256 tmp[4];
            for (int x = 0; x < UNROLL; x++) {
                tmp[x] = _mm256_setzero_ps();
            } 
            for (int k = 0; k < mat1->dim.cols; k++) {
                __m256 mat2_s = _mm256_broadcast_ss(&mat2->data[k][j]);
                for (int x = 0; x < UNROLL; x++) {
                    tmp[x] = _mm256_add_ps(tmp[x], _mm256_mul_ps(_mm256_load_ps(&mat1->data[i+x*8][k]), mat2_s));
                }
            }
            for (int x = 0; x < UNROLL; x++) {
                _mm256_store_ps(&dst->data[i+x*8][j], tmp[x]);
            }    
        }
    }    
}
编辑:
我试着用gdb找出哪一行导致了分段错误,结果看起来好像是
\u mm256\u load\u ps()
。我是否以错误的方式索引到矩阵,以致无法从正确的地址加载?还是内存对齐的问题

至少在一个地方,您正在以仅4个字节的跨距执行32字节对齐所需的加载。但我认为这不是你真正想做的:

for (int k = 0; k < mat1->dim.cols; k++) {
    for (int x = 0; x < UNROLL; x++) {
        ...
        _mm256_load_ps(&mat1->data[i+x*8][k])
     }
 }
for(int k=0;kdim.cols;k++){
对于(int x=0;x数据[i+x*8][k])
}
}
\u mm256\u load\u ps
加载8个连续的
浮点
s,即它将
数据[i+x*8][k]
加载到
数据[i+x*8][k+7]
我想您需要
数据[I+x][k*8]
并在最内部的循环中循环
k

如果需要未对齐的加载/存储,请使用
\u mm256\u loadu\u ps
/
\u mm256\u storeu\u ps
。但更喜欢将数据对齐到32B,并填充矩阵的存储布局,使行跨距为32字节的倍数。(数组的实际逻辑维度不必与跨距匹配;可以将每行末尾的填充保留为16或32字节的倍数。这使循环更易于写入。)


您甚至没有使用2D数组(您使用的是指向
float
数组的指针数组),但是语法看起来与
float a[100][100]
的语法相同,尽管asm中的含义非常不同。无论如何,在Fortran 2D数组中,索引是另一种方式,增加最左边的索引会将您带到内存中的下一个位置。但在C语言中,将左索引改为1会将您带到一个全新的行。(由
float**data
的不同元素指向,或者在一个适当的2D数组中,一行跨距)当然,由于这种混合使用
x*8
,您要跨过8行

说到asm,这段代码的结果非常糟糕,尤其是在gcc中,它为每个向量重新加载4个东西,我认为这是因为它不确定向量存储是否别名指针数据。为局部变量赋值,以确保编译器可以将它们从循环中提升出来。(例如,
const float*mat1dat=mat1->data;
)Clang做得稍微好一点,但是源代码中的访问模式本质上是不好的,需要对每个内部循环迭代进行指针跟踪才能到达新行,因为您循环了
x
,而不是
k
。我把它挂在墙上


但实际上,在尝试手动矢量化内存布局之前,您应该首先优化内存布局。可能值得转置其中一个数组,这样您就可以在连续内存中循环一个矩阵的行和另一个矩阵的列,同时计算行和列的点积以计算结果的一个元素。或者,它可能值得在一个内部循环中执行
c[Arow,Bcol]+=a_值,\u来自a*b[Arow,Bcol]
,而不是在前面进行转置(但这需要大量内存流量)。但无论您做什么,都要确保您没有跨过对内部循环中某个矩阵的非连续访问

您还需要放弃指针数组的做法,进行手动2D索引(
data[row*row\u stride+col]
),这样您的数据就都在一个连续的块中,而不是单独分配每一行。在您花费任何时间手动矢量化之前,首先进行此更改似乎最有意义

gcc或clang与
-O3
在自动矢量化标量C方面应该做得不错,特别是当您使用
-ffast math
编译时(手动矢量化完成后,您可以删除
-ffast math
,但在使用自动矢量化进行调优时使用它)

相关的:

  • 另请参阅我对的评论,了解另一个内存布局问题


您可以在查看缓存阻塞之前或之后手动进行矢量化,但当您执行此操作时,请参见。

能否显示
矩阵的定义
?提供相同
外部产品的2个副本的目的是什么?
?寻求调试帮助的问题(“此代码为什么不工作?”)必须包括所需的行为、特定的问题或错误以及在问题本身中重现问题所需的最短代码。没有明确问题陈述的问题对其他读者没有用处。请参阅:如何创建。@PhotometricStereoadded@DavidC.Rankin哦,那是个错误。编辑
for (int k = 0; k < mat1->dim.cols; k++) {
    for (int x = 0; x < UNROLL; x++) {
        ...
        _mm256_load_ps(&mat1->data[i+x*8][k])
     }
 }