Performance 通过std::vector进行矩阵乘法比numpy慢10倍

Performance 通过std::vector进行矩阵乘法比numpy慢10倍,performance,matrix,vector,scientific-computing,Performance,Matrix,Vector,Scientific Computing,尽管众所周知,使用嵌套的std::vector表示矩阵是非常困难的,但现在我们还是使用它吧,因为它非常灵活,而且许多现有函数都可以处理std::vector 我想,在小情况下,速度差可以忽略不计。但事实证明,vector比numpy.dot()慢10多倍 设A和B为大小为sizexsize的矩阵。假设平方矩阵只是为了简单。(我们不打算将讨论局限于平方矩阵的情况。)我们以确定的方式初始化每个矩阵,最后计算C=a*B 我们将“计算时间”定义为仅计算C=A*B所用的时间。换言之,各种间接费用不包括在内

尽管众所周知,使用嵌套的
std::vector
表示矩阵是非常困难的,但现在我们还是使用它吧,因为它非常灵活,而且许多现有函数都可以处理
std::vector

我想,在小情况下,速度差可以忽略不计。但事实证明,
vector
numpy.dot()
慢10多倍

A
B
为大小为
size
x
size
的矩阵。假设平方矩阵只是为了简单。(我们不打算将讨论局限于平方矩阵的情况。)我们以确定的方式初始化每个矩阵,最后计算
C=a*B

我们将“计算时间”定义为仅计算
C=A*B
所用的时间。换言之,各种间接费用不包括在内

Python3代码

将numpy导入为np
导入时间
导入系统
如果(len(sys.argv)!=2):
打印(“将`size`作为参数传递。”,file=sys.stderr);
系统出口(1);
size=int(sys.argv[1]);
A=np.ndarray((大小,大小));
B=np.ndarray((大小,大小));
对于范围内的i(尺寸):
对于范围内的j(尺寸):
A[i][j]=i*3.14+j
B[i][j]=i*3.14-j
开始=时间。时间()
C=np.点(A,B);
打印(“{.3e}”.format(time.time()-start),file=sys.stderr);
C++代码

使用名称空间std;
#包括
#包括
#包括
int main(int argc,字符**argv){
如果(argc!=2){
cerr最多快6.2倍(顺便说一句,我们当然使用
-O3

  • 优化3,加上通过引入指向
    a
    元素的指针来减少运算符
    []
    的调用,因为
    a
    的元素在展开循环中顺序访问。->最多快6.2倍,比优化4快一点。代码如下所示

  • g++
    -funroll循环
  • 循环展开
    的标志->无更改

  • g++->无变化

  • g++
    -flto
    打开链接时间优化的标志->无更改

  • ->没有变化

  • ->没有变化

  • 长线性
    std::vector
    而不是嵌套的
    std::vector
    ,交换计算顺序、块算法和部分展开->最多快2.2倍

  • 优化1,加->快4.7倍

  • 优化3,加上PGO->与优化3相同

  • 优化3,加上特定于g++的->与优化3相同


  • 现状 (原)13.06
    慢倍->(当前)
    2.10
    慢倍

    再次,您可以获得所有代码。但是我们引用一些代码,所有这些代码都是从C++代码的多线程版本调用的函数。 原始代码()

    void f(常量向量A、常量向量B、向量C、无符号行开始、无符号行结束){
    常量无符号j_max=B[0]。大小();
    常量无符号k_max=B.size();
    对于(int i=行开始;i<行结束;++i){
    对于(int j=0;j
    当前最佳代码()

    这是上面优化5的实现

    void f(常量向量A、常量向量B、向量C、无符号行开始、无符号行结束){
    静态常量unsigned num_unroll=5;
    常量无符号j_max=B[0]。大小();
    const unsigned k_max_for_unrolled_loop=B.size()/num_unroll*num_unroll;
    常量无符号k_max=B.size();
    对于(int i=行开始;i<行结束;++i){
    for(int k=0;k
    自从我们第一次发布这个问题以来,我们已经尝试了很多优化。我们花了整整两天的时间来解决这个问题,最终我们不知道如何优化当前最好的代码。我们怀疑像这样更复杂的算法会做得更好,因为我们处理的案例并不多,而且每个操作都在
    std::ve上ctor
    非常昂贵,正如我们所看到的,只需减少对
    []
    的调用就可以很好地提高性能


    不过,我们(希望)相信我们可以使它变得更好。

    矩阵乘法相对来说很容易优化。但是,如果您想获得适当的cpu利用率,这就变得很棘手,因为您需要对所使用的硬件有深入的了解。实现快速matmul内核的步骤如下:

  • 使用简单指令
  • 使用寄存器阻塞并一次获取多个数据
  • 针对您的chache系列进行优化(主要是L2和L3)
  • 并行化代码以使用多个线程
  • 在此链接下是一个非常好的资源,它解释了所有令人讨厌的细节:


    如果您需要更深入的建议,请留下评论。

    部分展开不需要事先知道大小。可能有一个小的剩余部分需要处理未展开的循环,但这也只影响一小部分时间:展开的速度仍然基本达到。@harold您完全正确。让我将您的评论链接到from OP.我有你可能感兴趣的,但没有一个是ar
    matrix_size: 200x200
    --------------- Time in seconds ---------------
    C++ (not multithreaded): 8.45e-03
             C++ (1 thread): 8.66e-03
            C++ (2 threads): 4.68e-03
            C++ (3 threads): 3.14e-03
            C++ (4 threads): 2.43e-03
                   Python 3: 4.07e-04
    -----------------------------------------------
    
    matrix_size: 400x400
    --------------- Time in seconds ---------------
    C++ (not multithreaded): 7.011e-02
             C++ (1 thread): 6.985e-02
            C++ (2 threads): 3.647e-02
            C++ (3 threads): 2.462e-02
            C++ (4 threads): 1.915e-02
                   Python 3: 1.466e-03
    -----------------------------------------------