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
xsize
的矩阵。假设平方矩阵只是为了简单。(我们不打算将讨论局限于平方矩阵的情况。)我们以确定的方式初始化每个矩阵,最后计算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内核的步骤如下:
如果您需要更深入的建议,请留下评论。部分展开不需要事先知道大小。可能有一个小的剩余部分需要处理未展开的循环,但这也只影响一小部分时间:展开的速度仍然基本达到。@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
-----------------------------------------------