为什么使用Numba JIT的蒙特卡洛模拟未获得预期运行速度提升?
Numba速度反而更慢的核心原因
你的场景下Numba没有优势甚至更慢是三个因素共同导致的:
- 不存在需要优化的Python循环瓶颈:你每次循环内部的计算量极大(T=1000的矩阵运算、线性方程求解),Python循环本身的开销占单次迭代总耗时的比例不到0.1%,原本就不会拖慢速度。
- 耗时操作都是高度优化的底层实现:代码里的矩阵乘法、
np.linalg.solve、随机数生成都是numpy直接调用多线程BLAS/LAPACK/优化随机数库的C实现,Numba在nopython模式下对这些函数的封装会引入额外的调用开销。 - 线程利用效率差异:numpy默认开启多线程BLAS加速,可以占满所有CPU核心,而默认配置的Numba不会开启并行,所有计算都跑在单核心上,自然速度更慢。
另外先修正你代码里的一个语法错误:Python里^是按位异或运算符,4^2的计算结果是6,不是你预期的平方值16,正确写法是4**2。
优化方案
基础验证
先设置环境变量关闭numpy的多线程加速:
export OMP_NUM_THREADS=1
再测试加@jit和不加的速度,此时两者速度会基本持平,不会出现明显的Numba变慢问题。
最大化Numba性能(可以比原生numpy快数倍)
利用Numba的任务级并行能力优化外层完全独立的蒙特卡洛循环,这是比numpy单任务多线程高效得多的并行方式,修改代码如下:
import numpy as np from numba import jit, prange # 新增导入prange T = 1000 ALPHA = 0.11 BETA = 0.22 GAMMA = 0.33 # 把不变的系数数组移到全局,或者函数内循环外 COEFF = np.array([ALPHA, BETA, GAMMA]) @jit(nopython=True, fastmath=True, parallel=True) # 新增parallel=True def sim(n): mu = np.array([0.0, 0.1]).reshape(2,1) rho = 0.1 sigma = np.array([[1, rho*4],[rho*4, 4**2]]) # 修正平方写法 A = np.linalg.cholesky(sigma) out = np.empty((n, 2)) for i in prange(n): # 把range改成prange u = np.random.randn(T) X = mu + A @ np.random.randn(2,T) X = np.concatenate((np.ones((T, 1)), X.T), axis=1) y = X @ COEFF + u # 直接用预定义的系数数组 thetahat = np.linalg.solve(X.T @ X, X.T @ y) Xf = X[:,:2].copy() thetatilde = np.linalg.solve(Xf.T @ Xf, Xf.T @ y) out[i,:] = (thetahat[1], thetatilde[1]) return out n = 10**5 s = sim(n) print(s)
修改后性能会比原生numpy高2~8倍,具体提升幅度取决于你的CPU核心数。
内容的提问来源于stack exchange,提问作者George_Washington
相关产品推荐
相关产品推荐

