numpy 矩阵代数或 python for-loop/list-comprehension 自动利用多处理?

numpy matrix algebra or python for-loop/list-comprehension automatically utilizes multiprocessing?

我一直认为python列表理解并没有隐含地利用多处理,阅读堆栈上的问题(例如this one)也给我同样的印象。然而,这是我的小实验:

import numpy as np
import time

# some arbitrary data
n = 1000
p = 5
X = np.block([[np.eye(p)], [np.zeros((n-p, p))]])
y = np.sum(X, axis=1) + np.random.normal(0, 1, (n, ))
n_loop = 100000

# run linear regression using direct matrix algebra
def in_sample_error_algebra(X, y):
    beta_hat = np.linalg.inv(X.transpose()@X)@(X.transpose()@y)
    y_hat = X@beta_hat
    error = metrics.mean_squared_error(y, y_hat)
    return error


start = time.time()
errors = [in_sample_error_algebra(X, y) for _ in range(n_loop)]
print('run time =', round(time.time() - start, 2), 'seconds')

run time = 19.68 seconds

虽然这段代码是 运行,但我的 CPU 的所有 6 个(物理)内核都达到了 100%

更神奇的是,当我从列表理解转为for循环时,同样的事情发生了。我想到 .append,它必须按顺序完成。见下文:

start = time.time()
errors = []
for _ in range(n_loop):
    errors.append(in_sample_error_algebra(X, y))
print('run time =', round(time.time() - start, 2), 'seconds')

run time = 21.29 seconds

有什么理论吗?

Python 3.7.2, numpy 1.15.4

的确,纯粹的 Python 计算无法从 多线程 中获益。 global interpreter lock (GIL) 防止多个线程同时访问解释器。

但是,多处理 在 Python 中是可能的,因为每个进程 运行 都有自己的 Python 解释器实例。这有性能成本:进程之间的初始化和数据共享不是免费的。通常这甚至不值得付出努力。

numpy 的情况有所不同。 Numpy 主要由用 C 编写的本机函数组成。当 C 代码暂时不需要解释器时,它可以释放 GIL 并同时允许不同的 Python 线程到 运行。 C 代码还可以生成 "non-Python" 个线程来并行计算。这就是 numpy 中发生的事情。

实际上,numpy 本身不会生成线程(我认为),但许多 matrix/vector 和线性代数例程会调用低级库 BLAS 和 LAPACK。这些库有多种实现方式,其中一些针对多线程进行了优化。您的 numpy 版本显然使用其中之一。

总之,外部列表理解和for循环运行都不是并行的,但是np.linalg.inv和矩阵乘积X @ beta_hat可能在内部运行 多线程。有关详细信息,请参阅 Parallel Programming with numpy and scipy