基于ATLAS和OpenBLAS基准测试numpy时的奇怪结果

9 投票
1 回答
955 浏览
提问于 2025-04-18 09:39

我在比较使用ATLAS和OpenBLAS的numpy性能时,发现了一些奇怪的结果,下面我来描述一下。

用来评估矩阵乘法(也叫做sgemm)的Python代码大致是这样的:

import sys
sys.path.insert(0, "numpy-1.8.1")

import numpy
import timeit

for i in range(100, 501, 100):
    setup = "import numpy; m1 = numpy.random.rand(%d, %d).astype(numpy.float32)" % (i, i)
    timer = timeit.Timer("numpy.dot(m1, m1)", setup)
    times = timer.repeat(100, 1)
    print "%3d" % i,
    print "%7.4f" % numpy.mean(times),
    print "%7.4f" % numpy.min(times),
    print "%7.4f" % numpy.max(times)

当我用numpy和ATLAS运行这个脚本时,测量的时间变化很大。第一列是矩阵的大小,后面是执行时间的平均值、最小值和最大值,这些时间是通过运行矩阵乘法100次得到的:

100  0.0003  0.0003  0.0004
200  0.0023  0.0010  0.0073
300  0.0052  0.0026  0.0178
400  0.0148  0.0066  0.0283
500  0.0295  0.0169  0.0531

如果我用numpy和OpenBLAS在一个线程下重复这个过程,运行时间就稳定多了:

100  0.0002  0.0002  0.0003
200  0.0014  0.0014  0.0015
300  0.0044  0.0044  0.0047
400  0.0102  0.0101  0.0105
500  0.0169  0.0168  0.0177

有没有人能解释一下这个现象?

补充信息:

对于ATLAS观察到的最小值和最大值并不是异常值,时间在给定范围内分布。

我在这里上传了i=500时ATLAS的时间数据:https://gist.github.com/uweschmitt/768bd165477d7c14095e

这些时间来自不同的运行,所以平均值、最小值和最大值会有些不同。

补充发现:

可能是CPU降频(http://www.scipy.org/scipylib/building/linux.html#step-1-disable-cpu-throttling)导致的?我对CPU降频了解不多,不知道它对我的测量结果有什么影响。遗憾的是,我无法在我的目标机器上设置或取消这个选项。

1 个回答

4

我无法重现这个问题,但我想我知道原因。我在一台64位的Linux电脑上使用的是Numpy 1.8.1。

首先,这是我用ATLAS得到的结果(最后一列是标准差):

100  0.0003  0.0002  0.0025  0.0003
200  0.0012  0.0010  0.0067  0.0006
300  0.0028  0.0026  0.0047  0.0004
400  0.0070  0.0059  0.0089  0.0004
500  0.0122  0.0109  0.0149  0.0009

接下来,这是用Anaconda提供的MKL得到的结果:

100  0.0003  0.0001  0.0155  0.0015
200  0.0005  0.0005  0.0006  0.0000
300  0.0018  0.0017  0.0021  0.0001
400  0.0039  0.0038  0.0042  0.0001
500  0.0079  0.0077  0.0084  0.0002

MKL的速度更快,但结果的分散程度是一样的。

ATLAS在编译时进行了调优,它会尝试不同的配置和算法,并为你的硬件选择最快的。如果你安装的是预编译版本,那么你使用的配置是为构建机器优化的,而不是为你的机器优化的。这种配置不当可能就是结果分散的原因。在我的情况下,我是自己编译的ATLAS。

相反,OpenBLAS是针对特定架构手动调优的,所以任何二进制安装都是等效的。MKL则是动态决定的。

如果我在从软件库安装的Numpy上运行脚本,并且链接的是预编译的ATLAS(没有激活SSE3),会发生这样的情况:

100  0.0007  0.0003  0.0064  0.0007
200  0.0021  0.0015  0.0090  0.0009
300  0.0050  0.0040  0.0114  0.0010
400  0.0113  0.0101  0.0186  0.0011
500  0.0217  0.0192  0.0329  0.0020

这些数字和你的数据更相似。

为了完整起见,我请朋友在她的机器上运行这个代码片段,她的Numpy是从Ubuntu软件库安装的,没有ATLAS,所以Numpy退回到了它的糟糕默认设置:

100  0.0007  0.0007  0.0008  0.0000
200  0.0058  0.0053  0.0107  0.0014
300  0.0178  0.0175  0.0188  0.0003
400  0.0418  0.0401  0.0528  0.0014
500  0.0803  0.0797  0.0818  0.0004

那么,可能发生了什么呢?

你可能安装的ATLAS不是最优的,这就是你得到这么分散结果的原因。我的数据是在一台1.7 GHz的Intel i5 CPU的笔记本电脑上运行的。我不知道你用的是什么机器,但我怀疑它不会比我的慢三倍。这表明ATLAS没有完全优化。

我怎么能确定呢?

运行numpy.show_config()可以告诉你它链接了哪些库,以及它们的位置。输出大概是这样的:

atlas_threads_info:
    libraries = ['lapack', 'ptf77blas', 'ptcblas', 'atlas']
    library_dirs = ['/usr/lib64/atlas-sse3']
    define_macros = [('ATLAS_INFO', '"\\"3.8.4\\""')]
    language = f77
    include_dirs = ['/usr/include']
blas_opt_info:

如果这是真的,怎么解决呢?

你可能有一个过时的预编译ATLAS二进制文件(它是某些软件包的依赖),或者你用来编译它的参数是错误的。最简单的解决办法是从源代码构建RMPS。这里有关于CentOS的说明。

请注意,OpenBLAS目前还不兼容multiprocessing,所以要注意这个限制。如果你在进行大量线性代数运算,MKL是最佳选择,但它价格不菲。学术界的人可以从Continuum Anaconda Python发行版中免费获得,许多大学也有校园许可证。

撰写回答