Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/139.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
比较Python、Numpy、Numba和C++;矩阵乘法_Python_C++_Numpy_Optimization_Numba - Fatal编程技术网

比较Python、Numpy、Numba和C++;矩阵乘法

比较Python、Numpy、Numba和C++;矩阵乘法,python,c++,numpy,optimization,numba,Python,C++,Numpy,Optimization,Numba,在我正在编写的程序中,我需要重复乘以两个矩阵。由于其中一个矩阵的大小,此操作需要一些时间,我想看看哪种方法最有效。矩阵的维数为(m x n)*(n x p),其中m=n=3和10^5

在我正在编写的程序中,我需要重复乘以两个矩阵。由于其中一个矩阵的大小,此操作需要一些时间,我想看看哪种方法最有效。矩阵的维数为
(m x n)*(n x p)
,其中
m=n=3
10^5

除了Numpy(我假设它与优化算法一起工作)之外,每个测试都包括以下的简单实现:

以下是我的各种实现:

Python

def dot_py(A,B):
    m, n = A.shape
    p = B.shape[1]

    C = np.zeros((m,p))

    for i in range(0,m):
        for j in range(0,p):
            for k in range(0,n):
                C[i,j] += A[i,k]*B[k,j] 
    return C
Numpy

def dot_np(A,B):
    C = np.dot(A,B)
    return C
Numba

代码与Python代码相同,但在使用前及时编译:

dot_nb = nb.jit(nb.float64[:,:](nb.float64[:,:], nb.float64[:,:]), nopython = True)(dot_py)
到目前为止,每个方法调用都使用
timeit
模块进行了10次计时。保持最佳结果。矩阵是使用
np.random.rand(n,m)
创建的

C++

mat2 dot(const mat2& m1, const mat2& m2)
{
    int m = m1.rows_;
    int n = m1.cols_;
    int p = m2.cols_;

    mat2 m3(m,p);

    for (int row = 0; row < m; row++) {
        for (int col = 0; col < p; col++) {
            for (int k = 0; k < n; k++) {
                m3.data_[p*row + col] += m1.data_[n*row + k]*m2.data_[p*k + col];
            }
        }
    }

    return m3;
}
编辑2

正如许多人所建议的,使用优化标志是匹配Numba的缺失元素。以下是新曲线与以前曲线的比较。标记为
v2
的曲线是通过切换两个内环获得的,显示出另外30%到50%的改善


一定要使用
-O3
进行优化。这将打开,这将大大加快代码的速度


Numba应该已经这样做了。

在当前的实现中,编译器很可能无法自动向量化最内部的循环,因为它的大小是3。此外,
m2
以“跳跃”方式访问。交换循环,以便在最内部的循环中迭代
p
,将使它工作得更快(
col
不会使数据访问“跳跃”,编译器应该能够做得更好(自动矢量化)

for(int行=0;行

在我的机器上,对于P=10 ^ 6个元素的原始C++实现,用<代码> g++dot.CPP-STD= C++ 11 -O3- O DOT/<代码>标志采用<代码> 12ms ,以上实现的交换循环采用<7> 如果你想获得最大的效率,你应该使用一个专用的线性代数库,经典的是/库。有许多实现,例如。您编写的内容不会超过超优化库

矩阵乘法将成为
dgemm
例程:d代表double,ge代表general,mm代表矩阵乘法。如果您的问题具有额外的结构,则可能会调用更具体的函数以获得额外的加速

请注意,Numpy dot已经调用了
dgemm
!你可能不会做得更好

为什么你的C++是慢的
与可能的情况相比,您的经典、直观的矩阵乘法算法速度较慢。编写利用处理器缓存等方式的代码。。。产生重要的性能增益。关键是,很多聪明的人都致力于使矩阵运算速度极快,你应该利用他们的工作,而不是重新发明轮子。

你仍然可以通过改进内存访问来优化这些循环,你的函数可能看起来像(假设矩阵是1000x1000):

说明:显然,循环i和ii一起执行的方式与我之前的相同,对j和k的保持相同,但这次大小为CSxCS的A和B中的区域可以保留在缓存中(我猜),并且可以多次使用


你可以玩CS和NCHUNK。对我来说,CS=10和NCHUNKS=100效果很好。当使用numba.jit时,它会将代码从7秒加速到850毫秒(注意,我使用的是1000x1000,上面的图形是用3x3x10^5运行的,所以这是另一种情况)。

这是令人惊讶的……我无法想象您会看到非常大的加速,但是您是否尝试过使用编译器优化标志,例如
-O3
?基本用法是:代码> G++*.CPP-STD= C++ 11 O3你也用Python调用这个C++函数,或者直接调用编译程序?@埃里克:这是希望,但没有理由用这种方式编写代码。有点像期待你的妻子整理你:-查找缓存小姐,这可能是你的C++失败的地方之一。@泰勒斯更新我的问题(见第二版),结果用<代码> -O3 。这就是你想要的吗?谢谢你的回答!我知道Numpy使用的是
dgemm
(事实上我已经看过Fortran代码)。出于这个原因,我希望它能表现得更好。为了简单起见,我使用了O(n^3)算法,因为我已经用它得到了比Numpy更好的结果。最终,我的代码将包含更多具有嵌套循环的自定义函数,这些函数在优化库中不可用,现在我对如何实现它们有了更好的想法。我认为优化的
dgemm
例程执行起来很幼稚,这主要是因为缓存和其他技术利用了处理器的实际工作方式,而不是O(n^3)位。不过我在细节方面不是专家。
#ifndef MAT2_H
#define MAT2_H

#include <iostream>

class mat2
{
private:
    int rows_, cols_;
    float* data_;

public: 
    mat2() {}                                   // (default) constructor
    mat2(int rows, int cols, float value = 0);  // constructor
    mat2(const mat2& other);                    // copy constructor
    ~mat2();                                    // destructor

    // Operators
    mat2& operator=(mat2 other);                // assignment operator

    float operator()(int row, int col) const;
    float& operator() (int row, int col);

    mat2 operator*(const mat2& other);

    // Operations
    friend mat2 dot(const mat2& m1, const mat2& m2);

    // Other
    friend void swap(mat2& first, mat2& second);
    friend std::ostream& operator<<(std::ostream& os, const mat2& M);
};

#endif
for (int row = 0; row < m; row++) {
    for (int k = 0; k < n; k++) {
        for (int col = 0; col < p; col++) {
            m3.data_[p*row + col] += m1.data_[n*row + k] * m2.data_[p*k + col];
        }
    }
}
CS = 10
NCHUNKS = 100

def dot_chunked(A,B):
    C = np.zeros(1000,1000)

    for i in range(NCHUNKS):
        for j in range(NCHUNKS):
            for k in range(NCHUNKS):
                for ii in range(i*CS,(i+1)*CS):
                    for jj in range(j*CS,(j+1)*CS):
                        for kk in range(k*CS,(k+1)*CS):
                            C[ii,jj] += A[ii,kk]*B[kk,jj] 
    return C