使用 cython 加速 numpy 矩阵乘法
Speed-up numpy matrix multiplication using cython
我在算法中计算了数千次矩阵乘法。因此,我计算:
import numpy as np
import time
def mat_mul(mat1, mat2, mat3, mat4):
return(np.dot(np.transpose(mat1),np.multiply(np.diag(mat2)[:,None], mat3))+mat4)
n = 2000
mat1 = np.random.rand(n,n)
mat2 = np.diag(np.random.rand(n))
mat3 = np.random.rand(n,n)
mat4 = np.random.rand(n,n)
t0=time.time()
cov_11=mat_mul(mat1, mat2, mat1, mat4)
t1=time.time()
print('time ',t1-t0, 's')
矩阵的大小:
n = (2000,2000) 并且 mat2 只有沿其对角线的条目。剩余条目为零。
在我的机器上,我得到以下信息:
time 0.3473696708679199 s
我怎样才能加快速度?
谢谢。
cython 不会加快它的速度,仅仅是因为 numpy 使用其他技巧来加快线程和 SIMD 之类的速度,任何试图仅使用 cython 实现此类功能的人最终都会获得更差的性能。
只有两种可能:
- 使用基于 gpu 的 numpy (cupy) 版本
- 如果您还没有使用最好的后端(如英特尔 MKL),请为 numpy 使用不同的更优化的后端
可以通过减少临时数组的数量并尽可能重用它们(即。多次)。事实上,虽然矩阵乘法通常是 heavily-optimized 通过 BLAS 实现,filling/copying(新分配的)数组增加了 non-negligible 开销。
实现如下:
def mat_mul_opt(mat1, mat2, mat3, mat4):
tmp1 = np.empty((n,n))
tmp2 = np.empty((n,n))
vect = np.diag(mat2)[:,None]
np.dot(np.transpose(mat1),np.multiply(vect, mat3, out=tmp1), out=tmp2)
np.add(mat4, tmp2, out=tmp1)
return tmp1
如果可以对输入矩阵进行变异,或者如果您可以 pre-allocate tmp1
和 tmp2
在函数外一次(然后多次重复使用),则可以进一步优化代码).这是一个例子:
def mat_mul_opt2(mat1, mat2, mat3, mat4, tmp1, tmp2):
vect = np.diag(mat2)[:,None]
np.dot(np.transpose(mat1),np.multiply(vect, mat3, out=tmp1), out=tmp2)
np.add(mat4, tmp2, out=tmp1)
return tmp1
以下是我的 i5-9600KF 处理器(6 核)的性能结果:
mat_mul: 103.6 ms
mat_mul_opt1: 96.7 ms
mat_mul_opt2: 83.5 ms
np.dot time only: 74.4 ms (kind of practical lower-bound)
Optimal lower bound: 55 ms (quite optimistic)
我在算法中计算了数千次矩阵乘法。因此,我计算:
import numpy as np
import time
def mat_mul(mat1, mat2, mat3, mat4):
return(np.dot(np.transpose(mat1),np.multiply(np.diag(mat2)[:,None], mat3))+mat4)
n = 2000
mat1 = np.random.rand(n,n)
mat2 = np.diag(np.random.rand(n))
mat3 = np.random.rand(n,n)
mat4 = np.random.rand(n,n)
t0=time.time()
cov_11=mat_mul(mat1, mat2, mat1, mat4)
t1=time.time()
print('time ',t1-t0, 's')
矩阵的大小: n = (2000,2000) 并且 mat2 只有沿其对角线的条目。剩余条目为零。
在我的机器上,我得到以下信息:
time 0.3473696708679199 s
我怎样才能加快速度?
谢谢。
cython 不会加快它的速度,仅仅是因为 numpy 使用其他技巧来加快线程和 SIMD 之类的速度,任何试图仅使用 cython 实现此类功能的人最终都会获得更差的性能。
只有两种可能:
- 使用基于 gpu 的 numpy (cupy) 版本
- 如果您还没有使用最好的后端(如英特尔 MKL),请为 numpy 使用不同的更优化的后端
可以通过减少临时数组的数量并尽可能重用它们(即。多次)。事实上,虽然矩阵乘法通常是 heavily-optimized 通过 BLAS 实现,filling/copying(新分配的)数组增加了 non-negligible 开销。
实现如下:
def mat_mul_opt(mat1, mat2, mat3, mat4):
tmp1 = np.empty((n,n))
tmp2 = np.empty((n,n))
vect = np.diag(mat2)[:,None]
np.dot(np.transpose(mat1),np.multiply(vect, mat3, out=tmp1), out=tmp2)
np.add(mat4, tmp2, out=tmp1)
return tmp1
如果可以对输入矩阵进行变异,或者如果您可以 pre-allocate tmp1
和 tmp2
在函数外一次(然后多次重复使用),则可以进一步优化代码).这是一个例子:
def mat_mul_opt2(mat1, mat2, mat3, mat4, tmp1, tmp2):
vect = np.diag(mat2)[:,None]
np.dot(np.transpose(mat1),np.multiply(vect, mat3, out=tmp1), out=tmp2)
np.add(mat4, tmp2, out=tmp1)
return tmp1
以下是我的 i5-9600KF 处理器(6 核)的性能结果:
mat_mul: 103.6 ms
mat_mul_opt1: 96.7 ms
mat_mul_opt2: 83.5 ms
np.dot time only: 74.4 ms (kind of practical lower-bound)
Optimal lower bound: 55 ms (quite optimistic)