指针和循环的结合有什么好处吗?

我正在研究Cheng的CUDA C编程,并遇到了这段代码:

void sumMatrixOnHost (float *A,float *B,float *C,const int nx,const int ny) {
    float *ia = A;
    float *ib = B;
    float *ic = C;
    for (int iy=0; iy<ny; iy++) {
        for (int ix=0; ix<nx; ix++) {
            ic[ix] = ia[ix] + ib[ix];
        }
        ia += nx; ib += nx; ic += nx;
    }
}

这是用于矩阵加法,从而矩阵以行主格式存储。

据我了解,内部for循环在一行上进行迭代并执行元素添加,然后使用外部for循环将指针递增到下一行的开始。

为什么这种方法比在整个矩阵上使用指针更好,即

for (int i=0; i<ny*nx; i++) {
    ic[i] = ia[i] + ib[i];
}

或双重for循环,即

for (int iy=0; iy<ny; iy++) {
    for (int ix=0; ix<nx; ix++) {
        ic[iy*nx+ix] = ia[iy*nx+ix] + ib[iy*nx+ix];
    }
}

这与编译器如何对其进行优化有关?

pl3000 回答:指针和循环的结合有什么好处吗?

最简单的方法始终是最好的方法:

for (int i=0; i<ny*nx; i++) {
    C[i] = A[i] + B[i];
}

这将比第一个解决方案快。将矩阵按行拆分的问题在于矢量化器会执行以下操作:

  • 以32字节为批处理的生产线(YMM大小)
  • 在行尾处理剩余的少数几个值。
  • 现在每行重复一次!

但是,如果您使用单个循环执行此操作,则生成的代码将是:

  • 以32字节(YMM的大小)为单位批量处理所有数据
  • 处理矩阵末端未与32字节块对齐的其余少数几个值。

第一个版本仅添加了无意义的代码来处理内部循环。不需要任何代码,只是破坏了对整个矩阵进行矢量化的功能。

,

sumMatrixOnHost上的方法最适合进行优化,它的执行速度(通常)应比您建议的两种方法更快。

对于alu乘法,比加法花费更多的时间。 因此,在sumMatrixOnHost中,没有

for (int i=0; i<ny*nx; i++) {
    ic[i] = ia[i] + ib[i];
 }

循环的每次迭代中都有乘法。 在

for (int iy=0; iy<ny; iy++) {
    for (int ix=0; ix<nx; ix++) {
        ic[iy*nx+ix] = ia[iy*nx+ix] + ib[iy*nx+ix];
    }
}

循环的每次迭代中都有3个乘法。

更简单的方法可以是

 int n = ny*nx;
 for (int i=0; i<n; i++) {
    ic[i] = ia[i] + ib[i];
 }

但是在最后一种方法中,我们失去了sumMatrixOnHost的另一优点,那就是能够对矩阵块进行运算,而不是对整个矩阵进行运算。

本文链接:https://www.f2er.com/3154848.html

大家都在问