获取 numpy.linalg.svd 和 numpy 矩阵乘法以使用多线程

Ind*_*ano 6 python multithreading numpy

我有一个使用大量 numpy 和 numpy.linalg 函数的脚本,经过一番研究后发现,据说它们会自动使用多线程。尽管如此,我的 htop 显示始终仅显示一个用于运行我的脚本的线程。

\n

我是多线程新手,现在我不知道如何正确设置它。

\n

我主要利用numpy.linalg.svd

\n

这是输出numpy.show_config()

\n
openblas64__info:\n    libraries = ['openblas64_', 'openblas64_']\n    library_dirs = ['/usr/local/lib']\n    language = c\n    define_macros = [('HAVE_CBLAS', None), ('BLAS_SYMBOL_SUFFIX', '64_'), ('HAVE_BLAS_ILP64', None)]\n    runtime_library_dirs = ['/usr/local/lib']\nblas_ilp64_opt_info:\n    libraries = ['openblas64_', 'openblas64_']\n    library_dirs = ['/usr/local/lib']\n    language = c\n    define_macros = [('HAVE_CBLAS', None), ('BLAS_SYMBOL_SUFFIX', '64_'), ('HAVE_BLAS_ILP64', None)]\n    runtime_library_dirs = ['/usr/local/lib']\nopenblas64__lapack_info:\n    libraries = ['openblas64_', 'openblas64_']\n    library_dirs = ['/usr/local/lib']\n    language = c\n    define_macros = [('HAVE_CBLAS', None), ('BLAS_SYMBOL_SUFFIX', '64_'), ('HAVE_BLAS_ILP64', None), ('HAVE_LAPACKE', None)]\n    runtime_library_dirs = ['/usr/local/lib']\nlapack_ilp64_opt_info:\n    libraries = ['openblas64_', 'openblas64_']\n    library_dirs = ['/usr/local/lib']\n    language = c\n    define_macros = [('HAVE_CBLAS', None), ('BLAS_SYMBOL_SUFFIX', '64_'), ('HAVE_BLAS_ILP64', None), ('HAVE_LAPACKE', None)]\n    runtime_library_dirs = ['/usr/local/lib']\nSupported SIMD extensions in this NumPy install:\n    baseline = SSE,SSE2,SSE3\n    found = SSSE3,SSE41,POPCNT,SSE42,AVX,F16C,FMA3,AVX2\n    not found = AVX512F,AVX512CD,AVX512_KNL,AVX512_KNM,AVX512_SKX,AVX512_CLX,AVX512_CNL,AVX512_ICL\n
Run Code Online (Sandbox Code Playgroud)\n

磁力RE

\n
import numpy as np \nimport tensorly as ty \n\n\ntensor = np.random.rand(32,32,32,32)\n\nunfolding = ty.unfold(tensor,0)\nunfolding = unfolding @ unfolding.transpose()\nU,S,_ = np.linalg.svd(unfolding)\n\n
Run Code Online (Sandbox Code Playgroud)\n

更新

\n

正如已接受的答案中所建议的,用 MKL 重建 numpy 解决了这个问题。

\n

Jér*_*ard 5

主要问题是矩阵的大小太小,线程在所有平台上都不值得。事实上,OpenBLAS 使用 OpenMP 创建有关矩阵大小的线程。线程通常创建一次,但对于机器来说,创建可能需要几十微秒到几十毫秒(在普通 PC 上通常需要数百微秒)。机器上的核心数量越大,要创建的线程数量就越多,因此开销也越大。当重用 OpenMP 线程池时,仍然需要支付开销,主要是由于线程之间的工作分配和同步,尽管开销通常要小得多(通常要小一个数量级)。

话虽这么说,当输出矩阵与输入矩阵相比很小时(这就是您的情况),OpenBLAS 显然会做出次优选择。事实上,OpenBLAS 在运行目标内核之前很难知道并行开销,因此它必须做出选择:通常根据输入矩阵的大小设置阈值,以便定义内核何时顺序执行或使用多个线程执行。这对于非常小的内核仍然保持快速以及巨大的内核与其他 BLAS 实现保持竞争力至关重要。问题是这个阈值的选择并不完美。看起来 OpenBLAS 只查看输出矩阵的大小,这对于代码中的“薄”矩阵来说显然不是最佳的(例如 50x1000000 @ 1000000x50)。实证分析表明,在您的情况下,阈值可以任​​意设置为 100x100:超过此阈值,OpenBLAS 将使用多个线程,否则不会。问题是,在大多数平台上,线程对于显着较小的矩阵已经很有用(例如,对于 64x64x64x64 张量)。

该阈值通过编译时定义进行调整,例如在gemm.c(或gemv.c )GEMM_MULTITHREAD_THRESHOLD中使用的定义。请注意,在代码中,k 维度很重要,但这不是我的机器上的基准测试显示的内容(可能是由于旧版本)正在使用的 OpenBLAS)。您可以使用较小的阈值(例如 1 而不是 4)重建 OpenBLAS。

另一种解决方案是使用另一种 BLAS 实现,例如BLISIntel MKL,它们应该使用不同的阈值(可能是更好的阈值)。最后一个解决方案是实现一个特定的实现来有效地计算代码的矩阵(可能使用 Numba 或 Cython),但 BLAS 实现经过大量优化,因此通常很难实际编写更快的代码(除非您非常熟悉低代码)级优化、编译器和现代处理器架构)。