Python多线程调用只读numpy array无并行加速问题如何解决
问题原因
- 你当前的代码瓶颈是Python层面实现的
for循环,运行时全程持有CPython的全局解释器锁(GIL),多线程场景下同一时间只有一个线程能拿到GIL执行计算,本质还是串行运行,自然没有并行加速效果。 - 就算是numpy原生操作,也只有底层C实现的运算逻辑才会主动释放GIL,Python层面的代码都受GIL限制。
优化方案
方案1:替换Python循环为numpy向量化操作(必做的基础优化)
你示例中的循环逻辑可以完全用numpy向量运算实现,避免GIL限制,性能可以提升几个数量级:
def run(shared_array, nb_iters): k = shared_array ** 2 k += 2 * nb_iters # 直接向量化计算,无Python层循环
如果是更复杂的逻辑无法直接向量化,参考下面的并行方案。
方案2:用多进程替代多线程
多进程每个进程拥有独立的GIL,不受GIL限制,适合CPU密集型计算场景,修改示例:
import multiprocessing import numpy as np import time shared_array = np.ones((5,5)) def run(nb_iters): # 只读小数据量数组无需特殊共享内存处理,直接访问即可 k = shared_array**2 for i in range(nb_iters): k +=2 def multi_process(): with multiprocessing.Pool(processes=5) as pool: pool.map(run, [1000000]*5) if __name__ == "__main__": t0 = time.time() multi_process() print(time.time() - t0) t0 = time.time() run(1000000*5) print(time.time() - t0)
如果是超大数组,可以用multiprocessing.Array配合numpy共享内存,避免多进程数据拷贝开销。
方案3:用Numba JIT编译释放GIL
Numba可以将Python函数编译为机器码,同时支持自动释放GIL和并行化,性能接近C语言,修改示例:
import numpy as np import time from numba import njit import threading shared_array = np.ones((5,5)) # 加该装饰器的函数会被编译为机器码,自动释放GIL @njit def run(shared_array, nb_iters): k = shared_array**2 for i in range(nb_iters): k +=2 def multi_thread(): jobs = [] for _ in range(5): thread = threading.Thread(target=run, args=(shared_array, 1000000)) jobs.append(thread) for j in jobs: j.start() for j in jobs: j.join() t0 = time.time() multi_thread() print(time.time() - t0) t0 = time.time() run(shared_array, 1000000 * 5) print(time.time() - t0)
如果是可并行的循环,还可以用@njit(parallel=True),直接在单函数内实现并行,无需手动开线程。
方案4:利用GPU计算
如果计算量极大,可以把numpy数组转换为PyTorch/TensorFlow张量,利用GPU的并行算力加速,比CPU多核效率更高。
内容的提问来源于stack exchange,提问作者MOHAMED HABI
相关产品推荐
相关产品推荐

