【问题标题】:Why MATLAB is faster than C++ in creating random numbers?为什么 MATLAB 在创建随机数方面比 C++ 快?
【发布时间】:2015-10-19 23:39:33
【问题描述】:

我在我的项目中使用 MATLAB 已经有一段时间了,但我几乎从未有过 C++ 方面的经验。

我需要速度,我听说 C++ 可以比 MATLAB 更高效、更快。所以我尝试了这个:

我在 MATLAB 上使用 rand(5000,5000) 创建了一个随机数矩阵。

在 C++ 中,我初始化了一个 2D 向量,创建了 2 个 for 循环,每个循环循环 5000 次,每次循环。 MATLAB 快 4-5 倍,所以我认为这是因为 matlab 并行执行矢量化代码,然后我使用 parallel_for 编写了 C++ 代码。代码如下:

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <fstream>
#include <ppl.h>
using namespace std;
using namespace concurrency;
int main();
{
    int a = 5000, b = 5000, j, k;
    vector< vector<int> > vec(a, vector<imt>(b));
    parallel_for(int(0), a, [&](int i) {
        for (j = 0; j <b; j++)
        {
            vec[i][j] = rand();
        }
    });
}

所以上面的代码比 MATLAB 的 rand(5000,5000) 快大约 25%,但 C++ 使用 100% 的 CPU,而 MATLAB 使用 30% 的 CPU。

所以我通过使用rand(5000,5000) 运行 3 个 MATLAB 实例来强制 MATLAB 使用所有 CPU,并将所用时间除以 3。它使 MATLAB 的速度是 C++ 的两倍。

我想知道我错过了什么?我知道这是一个小例子,但我需要一个答案才能确保将我的代码移植到 C++。

当前状态:

当我在没有parallel_for 的情况下编写 C++ 代码时,在 CPU 使用率相同的情况下,我得到了 MATLAB 一半的速度。然而,给出答案的人说他们几乎是一样的。我不明白我错过了什么

这是优化菜单的快照

【问题讨论】:

  • 可能不相关,只是好奇:您是否尝试过使用大小为 25000 的一维向量,然后在执行过程中将其伪装成二维?
  • 我需要一个答案才能确保将我的代码移植到 C++ 停止!别。 Matlab 的大多数核心计算例程已经用 C++(或另一种编译语言)编写,并且性能优于您自己编写的任何程序。其中许多已经是多线程的,如果你想编写更快的 C++,你也必须这样做。这里有很多关于 SO 的问题和答案与(通常)无用地追求编写比 Matlab 更快的代码有关。
  • 他说 我已经使用 Matlab 和 C++ 大约 10 年了,而你承认 我几乎从来没有使用过 C++。 这是一部分我的论点——你(或我)可能需要 10 年的 C++ 才能为 Matlab 提供的核心计算例程编写更快的代码。但这取决于你。
  • 您可能对Armadillo 库感兴趣。
  • (1) rand() 很烂。 (2) 我假设 C rand() 锁定了全局 RNG 状态,这会破坏您的并行性。给每个线程它自己的 random_device。另见stackoverflow.com/questions/7217791/random-numbers-in-c0x

标签: c++ performance matlab


【解决方案1】:

这可能不是答案,而是一个小提示。 由于使用了vectors,比较可能有点不公平。

这是我写的比较。两者都占用了四个可用线程之一的大约 100%。在这两种情况下,我都会创建 5000x5000 随机数并执行 100 次计时

Matlab

function stackoverflow

tic
for i=1:100
    A =rand(5000);
end
toc

运行时间:~27.9 秒

C++

#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <ctime>

using namespace std;


int main(){

    int N = 5000;
    double ** A = new double*[N];
    for (int i=0;i<N;i++)
        A[i] = new double[N];


    srand(time(NULL));

    clock_t start = clock();
    for (int k=0;k<100;k++){
        for (int i=0;i<N;i++){
            for (int j=0;j<N;j++){
                A[i][j] = rand();
            }
        }
    }

    cout << "T="<< (clock()-start)/(double)(CLOCKS_PER_SEC/1000)<< "ms " << endl;

}

运行时间:~28.7 秒

因此,两个示例的运行速度几乎相同。

【讨论】:

  • 我只是使用了相同的 cmets,没有任何变化。 Matlab 需要 38 秒,而 C++ 需要 80 秒才能完成。我错过了什么?我使用 Visual C++ 作为我的 IDE。
  • 我只是在linux机器上使用g++ filename.c
  • @ChrisDrew 是的,我在发布模式下使用并手动运行 exe 文件
  • 这两个随机数生成器是完全不同的算法,具有不同的输出和统计数据。 Matlab 的基于梅森捻线器的rand 在 (0,1) 上产生变量。 C++ 的基于线性同余生成器的rand 在 [0,RAND_MAX] 上产生变量。比较这些是没有意义的。
【解决方案2】:

当您在 Matlab 中调用 rand(5000,5000) 时,Matlab 通过调用 Intel MKL 库来执行命令,该库是用 C/C++ 编写的高度优化的库,包含大量手工编码的汇编。

MKL 应该比任何直接的 C++ 实现都快,但是 Matlab 调用外部库会产生开销。最终结果是,对于较小尺寸(例如小于 1K)的随机数生成,纯 C/C++ 实现会更快,但对于较大尺寸,Matlab 将受益于超级优化的 MKL。

【讨论】:

    【解决方案3】:

    查看@sonystarmap 的回答后,我添加了几种类型的容器:double*vector&lt;double&gt;vector&lt;vector&lt;double&gt; &gt;。我还添加了“指针容器”为 memset 的测试,因为 vector 初始化所有内存。

    使用这些优化标志编译 C++ 代码:-O3 -march=native

    结果:

    Matlab:经过的时间是 28.457788 秒。

    C++:

    T=23844.2ms

    T=25161.5ms

    T=25154ms

    T=24197.3ms

    T=24235.2ms

    T=24166.1ms

    我基本上找不到你提到的大收获。

    #include <iostream>
    #include <stdlib.h>
    #include <time.h>
    #include <ctime>
    #include <vector>
    #include <cstring>
    
    using namespace std;
    
    
    int main(){
    
        const int N = 5000;
    
        {
            vector<double> A(N*N);
    
            srand(0);
    
            clock_t start = clock();
            for (int k=0;k<100;k++){
                for (int i=0;i<N;i++){
                    for (int j=0;j<N;j++){
                        A[i*N+j] = rand();
                    }
                }
            }
    
            cout << "T="<< (clock()-start)/(double)(CLOCKS_PER_SEC/1000)<< "ms " << endl;
        }
    
        {
            vector<vector<double> > A(N);
            for (int i=0;i<N;i++)
                A[i] = vector<double>(N);
    
            srand(0);
    
            clock_t start = clock();
            for (int k=0;k<100;k++){
                for (int i=0;i<N;i++){
                    for (int j=0;j<N;j++){
                        A[i][j] = rand();
                    }
                }
            }
    
            cout << "T="<< (clock()-start)/(double)(CLOCKS_PER_SEC/1000)<< "ms " << endl;
        }
    
        {
            double ** A = new double*[N];
            for (int i=0;i<N;i++)
                A[i] = new double[N];
    
            srand(0);
    
            clock_t start = clock();
            for (int k=0;k<100;k++){
                for (int i=0;i<N;i++){
                    for (int j=0;j<N;j++){
                        A[i][j] = rand();
                    }
                }
            }
    
            cout << "T="<< (clock()-start)/(double)(CLOCKS_PER_SEC/1000)<< "ms " << endl;
        }
    
        {
            double ** A = new double*[N];
            for (int i=0;i<N;i++) {
                A[i] = new double[N];
                memset(A[i], 0, sizeof(double) * N);
            }
    
            srand(0);
    
            clock_t start = clock();
            for (int k=0;k<100;k++){
                for (int i=0;i<N;i++){
                    for (int j=0;j<N;j++){
                        A[i][j] = rand();
                    }
                }
            }
    
            cout << "T="<< (clock()-start)/(double)(CLOCKS_PER_SEC/1000)<< "ms " << endl;
        }
    
        {
            double * A = new double[N * N];
    
            srand(0);
    
            clock_t start = clock();
            for (int k=0;k<100;k++){
                for (int i=0;i<N;i++){
                    for (int j=0;j<N;j++){
                        A[i*N + j] = rand();
                    }
                }
            }
    
            cout << "T="<< (clock()-start)/(double)(CLOCKS_PER_SEC/1000)<< "ms " << endl;
        }
    
        {
            double * A = new double[N * N];
            memset(A, 0, sizeof(double) * N * N);
    
            srand(0);
    
            clock_t start = clock();
            for (int k=0;k<100;k++){
                for (int i=0;i<N;i++){
                    for (int j=0;j<N;j++){
                        A[i*N + j] = rand();
                    }
                }
            }
    
            cout << "T="<< (clock()-start)/(double)(CLOCKS_PER_SEC/1000)<< "ms " << endl;
        }
    }
    

    【讨论】:

    • 这是问题所在:我已经尝试过你的和 sonystarmap 的解决方案。我直接将它复制到我的视觉工作室。建造它。运行可执行文件。我的 C++ 结果是 83000 毫秒,而 Matlab 结果是 38 秒(38000 毫秒)我不明白为什么?
    • 您能提供您的项目设置吗?我开始认为您是在没有任何优化的情况下在调试模式下编译。
    • 这是我构建代码的屏幕照片。我复制了您的代码更改为从调试中发布。建造它。转到 .exe 所在的文件夹。运行它。这就是我所做的一切,我不知道还能做什么。你说了一些关于编译优化的事情,但我不知道该怎么做。 dropbox.com/s/hoxffuo3yeuabwe/Untitled.png?dl=0
    • 我对VS不太了解,我们需要编译器的参数,也许这是在项目属性下还是什么?
    【解决方案4】:
    #include <vector>
    #include <iostream>
    #include <cstdlib>
    #include <ctime>
    #include <cstring>
    
    int main() {
      const int N = 5000;
      std::vector<int> A(N*N);
      srand(0);
      clock_t start = clock();
      for(int k = 0; k < 100; ++k){
        for(int i = 0; i < N * N; ++i) {
            A[i] = rand();
        }
      }
      std::cout << (clock()-start)/(double)(CLOCKS_PER_SEC/1000) << "ms" << "\n";
      return 0;
    }
    

    从我的工作站上的 25-27 秒,编译器上没有任何优化标志到 21 秒

    -O3 -g -Wall -ftree-vectorizer-verbose=5 -msse -msse2 -msse3 -march=native -mtune=native -ffast-math

    【讨论】:

      猜你喜欢
      • 2016-07-07
      • 2021-01-22
      • 2015-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-28
      • 1970-01-01
      • 2017-06-07
      相关资源
      最近更新 更多