numpy 为什么linalg.solve不使用所有可用的线程

wecizke3  于 2023-04-12  发布在  其他
关注(0)|答案(1)|浏览(124)

我想知道为什么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

我将感谢任何帮助,以了解正在发生的事情,以及如何通过使用所有可用的线程来提高运行代码的性能。

oyxsuwqo

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 = 40N = 30,以下是我的i5- 9600 KF内核上OpenBLAS线程的调度(只有一部分持续0.5 ms,所以我们可以看到发生了什么):

每个彩色切片代表OpenBLAS线程的执行。黑色切片是空闲时间。大部分时间是空闲的,位于核心1上的主线程比其他线程做更多的工作,因为计算工作没有很好地平衡
如果你想让这个计算在你的用例中更快,这是最好只使用1个OpenBLAS线程在多个线程上运行不同的dgesv调用,而不是像OpenBLAS默认那样尝试并行化dgesv。虽然Numpy理论上可以做到这一点,Numpy还不打算使用多线程。唯一的并行函数主要是BLAS/LAPACK库之一,而不是直接使用Numpy。

相关问题