迭代已知维度矩阵的最快方法

时间:2011-02-18 20:52:46

标签: c++ c

我想知道迭代矩阵的最快方法是用c / c ++。

到目前为止,我提出的最佳方法是将矩阵映射到单个维度。

然后使用指针算法,可能更快的任何其他方法?

维度在运行时是已知的,但不是编译时间,矩阵是完全填充的。

#include <iostream>
#include <time.h>
#define XMAX 500
#define YMAX 400
#define ZMAX 300

int main()
{
    srand(0);   
    register double sum = 0;
    register int i;
    register int j;
    register int k;

    double *arr_ptr;
    arr_ptr = new double[XMAX*YMAX*ZMAX];

    for (i=0; i<XMAX*YMAX*ZMAX; ++i)
    {
        *(arr_ptr+i) = rand()/double(RAND_MAX);
    }

    clock_t start, finish;
    start = clock();

    for (i=0; i<XMAX; ++i)
    {
        for (j=0; j<YMAX; ++j)
        {
            for (k=0; k<ZMAX; ++k)
            {
                sum += *(arr_ptr+i*YMAX*ZMAX+j*ZMAX+k);
            }
        }
    }

    finish = clock();
    std::cout << "sum: " << sum << "\telapsed: " << finish - start << std::endl;
    std::cin.get();

    delete[] arr_ptr;
}

8 个答案:

答案 0 :(得分:5)

为什么要烦扰三个嵌套的for循环?你可以做到

for (i=0; i<XMAX*YMAX*ZMAX; ++i)
{
    sum += *(arr_ptr+i);
}

答案 1 :(得分:3)

根据ideone.com编译器,这比XMAX 500YMAX 400ZMAX 100的代码快650ms,运行100次。

double *p_current, *p_end;

p_current = arr_ptr;
p_end = (arr_ptr + XMAX*YMAX*ZMAX);
while(p_current != p_end) {
    sum += *p_current++;
}

请参阅:old versionnew version

答案 2 :(得分:2)

实际上并不重要,因为无论如何编译器都会优化它。因此arr[i][j][k]*(arr_ptr+i*YMAX*ZMAX+j*ZMAX+k)同样会很快。

答案 3 :(得分:1)

OpenCV使用指针算法:

double *ptr = arr_ptr;
for (i=0; i<XMAX*YMAX*ZMAX; ++i)
{
    sum += *ptr++;
}

我想它可能会以某种方式更快一点。试一试,告诉我们时间安排!

答案 4 :(得分:1)

double *ptr = arr_ptr;
for (int i=XMAX*YMAX*ZMAX; i>0; --i)
{
    sum += *ptr++;
} 

将循环变量与零进行比较,而不是某个常量可以为每次迭代节省一个或两个时钟周期(例如,在intel CPU上使用JNZ指令)

答案 5 :(得分:1)

在您的示例中,边界甚至是常量,因此正常的三维数组可以使用它,无论是C还是C ++。

然后,C和C ++实际上是关于动态分配的具有可变边界的数组的不同语言,不要混淆它们。对于C ++,使用向量类和类似的东西。它们是为此而制造的,它们应该是有效的。

在C中,由于C99有VLA,可变长度数组。与城市神话相反,如果你不将它们分配到堆栈中,它们可能非常有效。使用malloc作为C中的任何大块内存。

double (*arr_ptr)[XMAX][YMAX][ZMAX]
  = malloc(sizeof(*arr_ptr));

for (register size_t i=0; i<XMAX; ++i)
  for (register size_t j=0; j<YMAX; ++j)
    for (register size_t k=0; k<ZMAX; ++k)
       (*arr_ptr)[i][j][k] = rand()/double(RAND_MAX);

.

free(arr_ptr);

现代处理器具有相当复杂的寻址方案,因此可能没有必要有效地进行完整的索引计算。您的编译器通常比您更了解。

然后,为了提高效率,如何声明和处理循环变量可能会更加重要。使用正确的索引类型,size_t是正确的无符号类型。计算三维展平指数时,int可能很容易溢出,而且这里的签名类型没有多大意义。

然后将这些变量声明为尽可能本地化,使您和编译器更清楚。

register只是与编译器的契约,你永远不会获取这样一个索引的地址。通常这不会改善很多事情。但是,如果以后修改代码,可能会阻止您执行低效的操作。

最后但并非最不重要的是,如果您真的担心效率,请检查您的编译器产生的内容。 E.g gcc具有选项-S来生成中间汇编程序。阅读它而不是推测效率。

答案 6 :(得分:0)

首先需要说明的是,堆栈分配的多维数组以行主要顺序存储在内存中(在C和C ++中)。即,矩阵[2] [2] = {{1,2},{3,4}}将被存储在内存中,就像您实际声明的数组一样[4] = {1,2,3,4}而matrix[][]语法只是*( matrix + i * 2 + j)的语法糖。

因此,遍历矩阵的最快方法取决于您如何遍历矩阵:行主要顺序或列主要以及矩阵的大小:

  • 如果整个矩阵可以适合CPU缓存而不是遍历顺序无关紧要;
  • 如果矩阵大于CPU缓存而不是行进行主要遍历导致CPU缓存丢失次数减少。

了解矩阵运算中是否存在性能问题以及导致代码分析的最佳方法是分析代码。

答案 7 :(得分:0)

对于非常大的数据块,请考虑并行操作。在这种情况下,可以使用收集操作计算总和 - 其形式取决于您选择的并行框架。