犰狳库会减慢矩阵运算的执行速度吗?

计算科学 线性代数 矩阵 C++ 拉帕克 犰狳
2021-12-09 06:43:39

我已经将 MATLAB 代码转换为 C++ 以加快速度,使用 Armadillo 库处理 C++ 中的矩阵运算,但令人惊讶的是它比 MATLAB 代码慢 10 倍!

所以我测试了犰狳库,看看它是否是原因。下面的代码是一个简单的测试代码,它初始化两个矩阵,将它们相加并将结果保存到一个新矩阵中。一段代码使用了 Armadillo 库,而另一段则没有。使用犰狳的部分太慢了(注意经过的时间)。

它真的会减慢执行速度(尽管它应该加快执行速度)还是我错过了什么?

      #include<iostream>
      #include<math.h>
      #include<chrono>
      #include<armadillo>
      #include<time.h>
      #include<conio.h>
     using namespace std;
     using namespace arma;
     int main()
       {   
        srand((unsigned int)time(NULL));
        auto start = std::chrono::high_resolution_clock::now();
        double a[100][100];
        double b[100][100];
        double c[100][100];
        for (int i = 0; i < 100; i++)
           {
             for (int j = 0; j < 100; j++)
                {   
                 a[i][j] = rand() % 10;
                 b[i][j] = rand() % 10;
                 c[i][j] = a[i][j] + b[i][j];
                 }
            }

        auto finish = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> elapsed = finish - start;
        std::cout << "Elapsed time: " << elapsed.count() << " s\n";
        auto start1 = std::chrono::high_resolution_clock::now();
        mat a1= randi<mat>(100, 100, distr_param(1, 10));
        mat b1= randi<mat>(100, 100, distr_param(1, 10));
        mat c1=zeros(100,100);
        c1 = a1 + b1;
        auto finish1 = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> elapsed1 = finish1 - start1;
        std::cout << "Elapsed time: " << elapsed1.count() << " s\n";
        return 0;


    }

        }

这是我得到的答案:

Elapsed time: 0.00217959 s
Elapsed time: 0.00946147 s

如您所见,犰狳明显慢得多!不使用犰狳图书馆会更好吗?

2个回答

您正在比较堆栈中的静态定义数组:

    double a[100][100];
    double b[100][100];
    double c[100][100];

堆分配的数组:

    mat a1= randi<mat>(100, 100, distr_param(1, 10));
    mat b1= randi<mat>(100, 100, distr_param(1, 10));
    mat c1=zeros(100,100);

当然,堆栈分配的变量比堆分配的变量更受限制,但访问速度更快此外,这个循环:

             a[i][j] = rand() % 10;
             b[i][j] = rand() % 10;
             c[i][j] = a[i][j] + b[i][j];

实际上并不需要ab如果这些不是从函数返回的,那么正如@Kirill 在评论中提到的,一些编译器将完全远程这些数组并将其全部放入c.

(还要确保您正确编译。使用更高的优化级别标志,例如-O3,确保它不在调试模式等)

所以这个故事有一些道德。首先,使用堆栈分配的数组很棒。你应该尽可能地这样做。但其次,如果所有时间都花在 MATLAB 操作上,你不应该期望 MATLAB 与犰狳有很大不同,因为 MATLAB 本质上是一个用于调用诸如犰狳之类的东西的库。将 MATLAB 代码写成 for 循环会很慢,但它的向量化操作非常优化,并且多次隐式多线程. 虽然您没有在此处提供 MATLAB 代码来实际显示正在发生的事情,但隐式多线程可能是计时中的区别因素。当然,您始终可以在 C 中对自己的代码进行多线程和并行化,并获得同样好的效果,但是像 MATLAB 这样的矢量化编程语言的优势在于,简单函数的繁重工作已经为您完成了。

所以这实际上是说 MATLAB 向量化在处理堆上的操作时很好/很好,或者如果你想写出更复杂的循环,那么你应该尽可能尝试使用静态数组并且只去堆分配的数组必要时进行操作。不幸的是,在使用 MATLAB/Python/R 之类的东西时,您的操作是使用堆分配的变量,这就是为什么他们如此关注向量化和大型数组操作的原因。使用 C 或 Fortran 可以让您在这些语言所没有的堆与堆栈之间进行选择,而 Armadillo 等库则添加了类似 MATLAB 的功能。从另一个方向来看,像Julia这样的东西确实有像StaticArrays.jl这样的库来利用静态数组(和并行性),因此这是一种选择,既可以灵活地混合设计,又可以保持更高级别的语言。但无论您在哪里查看,优化的向量化堆操作最终可能会获得与 MATLAB 相同的速度:您必须自己编写更复杂的操作和循环才能产生速度差异。

您是否尝试过将 c1 初始化为内存?

c1=zeros(100,100);

我认为它不会将内存分配给变量,直到你填充它。

我运行您的代码时的结果:

Elapsed time: 0.0002079 s Elapsed time: 0.0003315 s

差异没有那么糟糕......我不是专家,但也许你的犰狳的安装方式?