我想知道为什么numpy的linalg.solve没有使用所有可用的线程来做它的演算。
我用它来解决一个多维系统,在某种程度上,它应该解决找到一个向量与27个条目n.N.N
次。所以,因为它必须做同样类型的计算很多次,我以为它会利用所有可用的线程,但它不是实际发生的事情。它只使用16个线程,即使我改变n
,我已经测试了n = 300和400。我还尝试从threadpoolctl手动指定线程,它只会减少运行线程的数量,但没有看到性能上的差异(不知道为什么)。
测试代码为
import numpy as np
from numpy import *
from timeit import default_timer as timer
import math;
import os, psutil
process = psutil.Process(os.getpid())
###### Main time loop ##########################################################
n = 400
N = 300
s = timer()
k_star = np.random.rand(27*n*N*N).reshape((27,n,N,N))
T = np.random.rand(27*27*n*N*N).reshape((27,27,n,N,N))
t1 = timer() - s
print("time to allocate k_star and T = " + str(t1))
for time in range(0,1001):
s = timer()
fout = np.transpose(linalg.solve(np.transpose(T),np.transpose(k_star)))
t = timer() - s
print("time to calculate fout solving the system = " + str(t))
print(time)
if time%1000 == 0:
memory = process.memory_info().rss/1000000000
print("RAM Memory occupied = " + str(round(memory,2)) + " Gb")
可能对答案有用,np.__config__.show()
的输出是
openblas64__info:
libraries = ['openblas64_', 'openblas64_']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None), ('BLAS_SYMBOL_SUFFIX', '64_'), ('HAVE_BLAS_ILP64', None)]
runtime_library_dirs = ['/usr/local/lib']
blas_ilp64_opt_info:
libraries = ['openblas64_', 'openblas64_']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None), ('BLAS_SYMBOL_SUFFIX', '64_'), ('HAVE_BLAS_ILP64', None)]
runtime_library_dirs = ['/usr/local/lib']
openblas64__lapack_info:
libraries = ['openblas64_', 'openblas64_']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None), ('BLAS_SYMBOL_SUFFIX', '64_'), ('HAVE_BLAS_ILP64', None), ('HAVE_LAPACKE', None)]
runtime_library_dirs = ['/usr/local/lib']
lapack_ilp64_opt_info:
libraries = ['openblas64_', 'openblas64_']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None), ('BLAS_SYMBOL_SUFFIX', '64_'), ('HAVE_BLAS_ILP64', None), ('HAVE_LAPACKE', None)]
runtime_library_dirs = ['/usr/local/lib']
Supported SIMD extensions in this NumPy install:
baseline = SSE,SSE2,SSE3
found = SSSE3,SSE41,POPCNT,SSE42,AVX,F16C,FMA3,AVX2,AVX512F,AVX512CD,AVX512_SKX,AVX512_CLX
not found = AVX512_KNL,AVX512_KNM,AVX512_CNL,AVX512_ICL
我将感谢任何帮助,以了解正在发生的事情,以及如何通过使用所有可用的线程来提高运行代码的性能。
1条答案
按热度按时间oyxsuwqo1#
你使用4D/5D矩阵,在最后一个维度中有相当少量的项目。问题是Numpy的当前实现在这种情况下效率很低。
实际上,
linalg.solve
在内部调用solve
(以及与用例类似的solve1
),它将ND数组线性化,以便在顺序循环中多次调用dgesv
。您使用OpenBLAS实现,它能够使用多个线程并行计算dgesv
。然而,dgesv
是一个LAPACK原语,而不是真正的BLAS原语。OpenBLAS主要是一个BLAS实现,尽管它实现了一些LAPACK原语。它并不意味着非常有效地计算LAPACK原语。事实上,只有少数库可以这样做(这实际上很难做到)。尽管如此,这里问题是Numpy进行了大量LAPACK调用,每个dgesv
调用只持续很短的时间。因此OpenBLAS不能完全受益于多核处理器,因为它需要大量的时间(即至少几微秒)来与其他核心共享工作,等待它们来完成工作,实际上核心数量越多并行开销越高毕竟如果你想组织一个有100个朋友的聚会一般来说,组织聚会比和一个朋友一起做要花更多的时间,如果聚会持续10分钟,组织聚会的时间就会比聚会的时间大得多。使用
n = 40
和N = 30
,以下是我的i5- 9600 KF内核上OpenBLAS线程的调度(只有一部分持续0.5 ms,所以我们可以看到发生了什么):每个彩色切片代表OpenBLAS线程的执行。黑色切片是空闲时间。大部分时间是空闲的,位于核心1上的主线程比其他线程做更多的工作,因为计算工作没有很好地平衡。
如果你想让这个计算在你的用例中更快,这是最好只使用1个OpenBLAS线程和在多个线程上运行不同的
dgesv
调用,而不是像OpenBLAS默认那样尝试并行化dgesv
。虽然Numpy理论上可以做到这一点,Numpy还不打算使用多线程。唯一的并行函数主要是BLAS/LAPACK库之一,而不是直接使用Numpy。