如何在Python中加速矩阵乘法?

-1 投票
1 回答
60 浏览
提问于 2025-04-14 18:09

我现在正在编写一个模拟程序,并且在优化速度。通过查看性能分析工具line_profiler,我发现我的瓶颈在于矩阵乘法(np.matmul)。我需要对一个矩阵'A'(大小为[990, 1, 10, 3, 3])和一个矩阵'B'(大小为[990, 1, 10, 3, 1])进行大约80万次不同的乘法运算,而每次乘法的A和B都是不同的。下面是一些简化的“测试”代码(在这个简单的例子中,矩阵保持不变,所以请不要太关注这一点):

precession = np.random.uniform(1,10,[990, 1, 10, 3, 3])
vecMblood = np.random.uniform(1,10,[990, 1, 10, 3, 1])

start = timeit.default_timer()
for _ in range(4000*200):
    np.matmul(precession, vecMblood)
end = timeit.default_timer()
print(f"np.matmul took {end - start} seconds.")

在单核处理器上,这个过程需要133秒。我在主代码中使用了多线程,这确实提高了一些速度——也就是说,在原始代码中,有4000次乘法是顺序进行的,但在200个测试案例中是独立的——因此这4000次乘法在12个核心上顺序运行,总共是4000x200(800,000)。我还将主代码中的数据类型从float64减少到float32,这也有所帮助。不过,除了np.matmul之外,Python中有没有更优化的方式来处理这个问题?我也尝试在其他地方实现Numba函数,但开销太大了——所以我在寻找一种没有这种开销的方法。

1 个回答

1

你可以通过使用 einsum 来让这个过程快大约40%。

np.einsum("...ij,...j", precession, vecMblood[..., 0])[..., None]

时间记录:

import numpy as np

rng = np.random.default_rng()

a = rng.random([990, 1, 10, 3, 3])
b = rng.random(([990, 1, 10, 3, 1]))

%timeit -n 1000 np.matmul(a, b)
%timeit -n 1000 np.einsum("...ij,...j", a, b[..., 0])[..., None]

输出结果:

190 µs ± 10 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
108 µs ± 816 ns per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

撰写回答