【发布时间】:2016-07-31 07:36:59
【问题描述】:
在我正在处理的程序中,我需要重复将两个矩阵相乘。由于其中一个矩阵的大小,此操作需要一些时间,我想看看哪种方法最有效。矩阵的维度为(m x n)*(n x p),其中m = n = 3 和10^5 < p < 10^6。
除了我认为使用优化算法的 Numpy 之外,每个测试都包含 matrix multiplication 的简单实现:
以下是我的各种实现:
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;
}
这里,mat2 是我定义的自定义类,dot(const mat2& m1, const mat2& m2) 是该类的友元函数。它使用来自Windows.h 的QPF 和QPC 进行计时,并使用MinGW 和g++ 命令编译程序。同样,保留了从 10 次执行中获得的最佳时间。
结果
正如预期的那样,简单的 Python 代码速度较慢,但对于非常小的矩阵,它仍然胜过 Numpy。在最大规模的情况下,Numba 比 Numpy 快约 30%。
我对 C++ 结果感到惊讶,其中乘法所需的时间几乎比 Numba 多一个数量级。事实上,我预计这些将花费类似的时间。
这引出了我的主要问题:这正常吗?如果不正常,为什么 C++ 比 Numba 慢?我刚开始学习 C++,所以我可能做错了什么。如果是这样,我的错误是什么,或者我可以做些什么来提高我的代码效率(除了选择更好的算法)?
编辑 1
这是mat2 类的标题。
#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
编辑 2
正如许多人所建议的,使用优化标志是匹配 Numba 的缺失元素。下面是与以前的曲线相比的新曲线。标记为v2 的曲线是通过切换两个内循环获得的,显示出另外30% 到50% 的改进。
【问题讨论】:
-
这令人惊讶...我无法想象您会看到极大的加速,但您是否尝试过使用编译器优化标志,例如
-O3?基本用法是g++ *.cpp -std=c++11 -O3 -
另外你是在以任何方式from python调用这个c++函数还是直接调用一个编译好的程序?
-
@Eric:这是一个希望,但没有理由以这种方式编写代码。有点像期待你的妻子在你之后收拾东西:-)
-
查找缓存未命中,这可能是您的 C++ 失败的地方之一。
-
@TylerS 我用
-O3更新了我的问题(见第二次编辑)。这是你要找的吗?
标签: python c++ numpy optimization numba