基于ATLAS和OpenBLAS基准测试numpy时的奇怪结果
我在比较使用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 个回答
我无法重现这个问题,但我想我知道原因。我在一台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发行版中免费获得,许多大学也有校园许可证。