np.linalg.inv仍然持有GIL?使用ThreadPoolExecutor和numpy矩阵逆

ff29svar  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(85)

我知道大多数numpy操作都会释放GIL。因此,当使用python的多线程时,大多数操作都可以获得预期的速度提升。但我发现奇怪的是,numpy.linalg.inv的情况并非如此。

实验

我尝试了以下方法

import numpy as np
import time
from concurrent.futures import ThreadPoolExecutor

def numpy_op(arr):
    # do matrix inversion here
    return np.linalg.inv(arr)

num_workers = 8
np.random.seed(42)
args = [np.random.randn(10000, 10000) for _ in range(num_workers)]

# parallelize with thread pool
s_time = time.time()
with ThreadPoolExecutor(max_workers=num_workers) as executor:
    res = list(executor.map(numpy_op, args))

# sequential code
res = []
for arg in args:
    res.append(numpy_op(arg))
print(f'time consumed: {time.time()-s_time:.2f}s')

字符串
结果

  • 多线程:36.44秒
  • 连续:28.14s

然而,如果我做一些除了矩阵求逆之外的其他numpy操作,例如

def numpy_op(arr):
    # some random numpy operations here
    return (arr**2 + 2) ** 0.5


通过这些操作,结果是

  • 多线程:0.62s
  • 连续:3.71秒

版本

我正在使用Python 3.9.10numpy 1.24.3

2ic8powd

2ic8powd1#

这里的问题是np.linalg.inv的实现本身是多线程的。要查看此效果,请在示例的顶部添加以下行:

import os

os.environ["OPENBLAS_NUM_THREADS"] = "1"

字符串
这将可用于此类操作的线程数量减少到只有一个(假设您的numpy是使用openBLAS构建的。其他情况请参见threadpoolctl)。
在我的机器上,这个变化翻转了结果:

  • 顺序:31.5 s -> 64.5 s
  • 多线程:56.0 s -> 26.8 s

我不知道numpy中是否有一个多线程操作的完整列表,但在优化多核吞吐量时,这是需要考虑的。

相关问题