Python/Numpy中的多线程BLAS

50 投票
4 回答
35871 浏览
提问于 2025-04-16 13:26

我正在尝试在Python中实现大量的矩阵乘法。最开始,我以为NumPy会自动使用我安装的多线程BLAS库,因为我在安装时是针对这些库进行配置的。不过,当我查看top或者其他工具时,似乎代码根本没有使用多线程。

有没有什么想法,是什么问题,或者我该怎么做才能轻松利用BLAS的性能呢?

4 个回答

3

可能是因为矩阵乘法在内存使用上有一定限制,所以在同样的内存环境下增加更多的处理核心并不会带来太大的提升。当然,如果你在使用Fortran实现时看到明显的速度提升,那我可能就错了。

我理解,对于这类问题来说,合理的缓存管理比计算能力更为重要。BLAS库大概就是为你处理这个问题的。

你可以试着安装一下Enthought的Python发行版来做个简单的对比。他们使用了Intel的数学核心库,我相信这个库可以利用多个核心,如果有的话。

116

我之前在另一个帖子里发过这个,但我觉得在这里更合适:

更新(2014年7月30日):

我在我们的新高性能计算机(HPC)上重新进行了基准测试。 硬件和软件的配置都与原始答案中的设置不同。

我把结果放在了一个谷歌表格中(也包含了原始答案的结果)。

硬件

我们的高性能计算机有两个不同的节点,一个是使用英特尔的Sandy Bridge处理器,另一个是使用更新的Ivy Bridge处理器:

Sandy(MKL、OpenBLAS、ATLAS):

  • CPU: 2 x 16 英特尔(R) 至强(R) E2560 Sandy Bridge @ 2.00GHz(16个核心)
  • 内存: 64 GB

Ivy(MKL、OpenBLAS、ATLAS):

  • CPU: 2 x 20 英特尔(R) 至强(R) E2680 V2 Ivy Bridge @ 2.80GHz(20个核心,开启超线程后为40个核心)
  • 内存: 256 GB

软件

两个节点的软件配置是一样的。使用的是OpenBLAS而不是GotoBLAS2,并且还有一个设置为8线程的多线程ATLAS BLAS。

  • 操作系统: Suse
  • 英特尔编译器: ictce-5.3.0
  • Numpy: 1.8.0
  • OpenBLAS: 0.2.6
  • ATLAS:: 3.8.4

点积基准测试

基准测试代码与下面的相同。不过对于新机器,我还对50008000的矩阵大小进行了基准测试。
下面的表格包含了原始答案中的基准测试结果(重命名为:MKL --> Nehalem MKL,Netlib Blas --> Nehalem Netlib BLAS,等等)

矩阵乘法(大小=[1000,2000,3000,5000,8000])

单线程性能: 单线程性能

多线程性能(8线程): 多线程(8线程)性能

线程与矩阵大小(Ivy Bridge MKL): 矩阵大小与线程

基准测试套件

基准测试套件

单线程性能: 在这里输入图像描述

多线程(8线程)性能: 在这里输入图像描述

结论

新的基准测试结果与原始答案中的结果相似。OpenBLASMKL的性能在同一水平,唯一的例外是特征值测试。 在单线程模式下,特征值测试在OpenBLAS上的表现还算不错。 但在多线程模式下,性能就差了。

“矩阵大小与线程图表”也显示,虽然MKL和OpenBLAS通常在核心/线程数量上扩展良好,但这取决于矩阵的大小。对于小矩阵,增加更多核心不会显著提高性能。

Sandy BridgeIvy Bridge的性能提升大约为30%,这可能是由于更高的时钟频率(+0.8 GHz)和/或更好的架构。


原始答案(2011年10月4日):

不久前,我需要优化一些用Python编写的线性代数计算/算法,这些算法使用了numpy和BLAS,所以我对不同的numpy/BLAS配置进行了基准测试。

具体来说,我测试了:

  • Numpy与ATLAS
  • Numpy与GotoBlas2(1.13)
  • Numpy与MKL(11.1/073)
  • Numpy与加速框架(Mac OS X)

我进行了两个不同的基准测试:

  1. 不同大小矩阵的简单点积
  2. 可以在这里找到的基准测试套件。

以下是我的结果:

机器

Linux(MKL、ATLAS、无MKL、GotoBlas2):

  • 操作系统: Ubuntu Lucid 10.4 64位。
  • CPU: 2 x 4 英特尔(R) 至强(R) E5504 @ 2.00GHz(8个核心)
  • 内存: 24 GB
  • 英特尔编译器: 11.1/073
  • Scipy: 0.8
  • Numpy: 1.5

Mac Book Pro(加速框架):

  • 操作系统: Mac OS X Snow Leopard(10.6)
  • CPU: 1 英特尔双核2.93 GHz(2个核心)
  • 内存: 4 GB
  • Scipy: 0.7
  • Numpy: 1.3

Mac Server(加速框架):

  • 操作系统: Mac OS X Snow Leopard Server(10.6)
  • CPU: 4 x 英特尔(R) 至强(R) E5520 @ 2.26 GHz(8个核心)
  • 内存: 4 GB
  • Scipy: 0.8
  • Numpy: 1.5.1

点积基准测试

代码:

import numpy as np
a = np.random.random_sample((size,size))
b = np.random.random_sample((size,size))
%timeit np.dot(a,b)

结果:

    System        |  size = 1000  | size = 2000 | size = 3000 |
netlib BLAS       |  1350 ms      |   10900 ms  |  39200 ms   |    
ATLAS (1 CPU)     |   314 ms      |    2560 ms  |   8700 ms   |     
MKL (1 CPUs)      |   268 ms      |    2110 ms  |   7120 ms   |
MKL (2 CPUs)      |    -          |       -     |   3660 ms   |
MKL (8 CPUs)      |    39 ms      |     319 ms  |   1000 ms   |
GotoBlas2 (1 CPU) |   266 ms      |    2100 ms  |   7280 ms   |
GotoBlas2 (2 CPUs)|   139 ms      |    1009 ms  |   3690 ms   |
GotoBlas2 (8 CPUs)|    54 ms      |     389 ms  |   1250 ms   |
Mac OS X (1 CPU)  |   143 ms      |    1060 ms  |   3605 ms   |
Mac Server (1 CPU)|    92 ms      |     714 ms  |   2130 ms   |

点积基准测试 - 图表

基准测试套件

代码:
有关基准测试套件的更多信息,请参见这里

结果:

    System        | eigenvalues   |    svd   |   det  |   inv   |   dot   |
netlib BLAS       |  1688 ms      | 13102 ms | 438 ms | 2155 ms | 3522 ms |
ATLAS (1 CPU)     |   1210 ms     |  5897 ms | 170 ms |  560 ms |  893 ms |
MKL (1 CPUs)      |   691 ms      |  4475 ms | 141 ms |  450 ms |  736 ms |
MKL (2 CPUs)      |   552 ms      |  2718 ms |  96 ms |  267 ms |  423 ms |
MKL (8 CPUs)      |   525 ms      |  1679 ms |  60 ms |  137 ms |  197 ms |  
GotoBlas2 (1 CPU) |  2124 ms      |  4636 ms | 147 ms |  456 ms |  743 ms |
GotoBlas2 (2 CPUs)|  1560 ms      |  3278 ms | 116 ms |  295 ms |  460 ms |
GotoBlas2 (8 CPUs)|   741 ms      |  2914 ms |  82 ms |  262 ms |  192 ms |
Mac OS X (1 CPU)  |   948 ms      |  4339 ms | 151 ms |  318 ms |  566 ms |
Mac Server (1 CPU)|  1033 ms      |  3645 ms |  99 ms |  232 ms |  342 ms |

基准测试套件 - 图表

安装

安装MKL包括安装完整的英特尔编译器套件,这个过程相对简单。不过,由于一些错误/问题,配置和编译支持MKL的numpy有点麻烦。

GotoBlas2是一个小包,可以很容易地编译成共享库。不过,由于一个错误,你必须在构建后重新创建共享库才能与numpy一起使用。
此外,出于某种原因,针对多个目标平台的构建没有成功。因此,我不得不为每个平台创建一个.so文件,以便获得优化后的libgoto2.so文件。

如果你从Ubuntu的仓库安装numpy,它会自动安装并配置numpy以使用ATLAS。从源代码安装ATLAS可能需要一些时间,并且需要一些额外的步骤(如Fortran等)。

如果你在Mac OS X机器上使用FinkMac Ports安装numpy,它会将numpy配置为使用ATLAS苹果的加速框架。 你可以通过运行ldd命令查看numpy.core._dotblas文件,或者调用numpy.show_config()来检查。

结论

MKL的表现最好,其次是GotoBlas2
特征值测试中,GotoBlas2的表现出乎意料地差,不知道为什么会这样。
苹果的加速框架在单线程模式下表现非常好(与其他BLAS实现相比)。

GotoBlas2MKL在线程数量上扩展得很好。所以如果你需要处理大矩阵,使用多线程会有很大帮助。

无论如何,不要使用默认的netlib blas实现,因为它对于任何严肃的计算工作来说都太慢了。

在我们的集群上,我还安装了AMD的ACML,性能与MKLGotoBlas2相似,但我没有具体数据。

我个人建议使用GotoBlas2,因为它更容易安装且是免费的。

如果你想用C++/C编程,也可以看看Eigen3,它在某些情况下的表现优于MKL/GotoBlas2,而且使用起来也很简单。

18

并不是NumPy里的所有功能都使用BLAS库,只有一些特定的函数,比如 dot()vdot()innerproduct(),还有一些来自 numpy.linalg 模块的函数。此外,要注意的是,对于大数组来说,很多NumPy操作受到内存带宽的限制,所以即使有优化的实现,也不太可能带来什么提升。如果你的内存带宽受到很大限制,使用多线程是否能提高性能,主要还是看你的硬件配置。

撰写回答