基准测试(python 与使用 BLAS 的 c + +)和(numpy)

我想写一个程序,使广泛使用 BLAS 和 LAPACK 线性代数功能。由于性能是一个问题,我做了一些基准测试,并想知道,如果我采取的方法是合法的。

I have, so to speak, three contestants and want to test their performance with a simple matrix-matrix multiplication. The contestants are:

  1. Numpy,仅使用 dot的功能。
  2. Python,通过共享对象调用 BLAS 功能。
  3. C + + ,通过共享对象调用 BLAS 功能。

场景

我为不同维度的 i实现了一个矩阵-矩阵乘法。i从5增加到500,矩阵 m1m2是这样设置的:

m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)

1. Numpy

使用的代码如下:

tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))

2. Python, calling BLAS through a shared object

用函数

_blaslib = ctypes.cdll.LoadLibrary("libblas.so")
def Mul(m1, m2, i, r):


no_trans = c_char("n")
n = c_int(i)
one = c_float(1.0)
zero = c_float(0.0)


_blaslib.sgemm_(byref(no_trans), byref(no_trans), byref(n), byref(n), byref(n),
byref(one), m1.ctypes.data_as(ctypes.c_void_p), byref(n),
m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(zero),
r.ctypes.data_as(ctypes.c_void_p), byref(n))

测试代码如下:

r = numpy.zeros((i,i), numpy.float32)
tBlas = timeit.Timer("Mul(m1, m2, i, r)", "import numpy; from __main__ import i, m1, m2, r, Mul")
rBlas.append((i, tBlas.repeat(20, 1)))

3. c + + ,通过共享对象调用 BLAS

现在 c + + 代码自然要长一些,因此我将信息减少到最少。
我用

void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");

我用 gettimeofday测量时间是这样的:

gettimeofday(&start, NULL);
f(&no_trans, &no_trans, &dim, &dim, &dim, &one, A, &dim, B, &dim, &zero, Return, &dim);
gettimeofday(&end, NULL);
dTimes[j] = CalcTime(start, end);

where j is a loop running 20 times. I calculate the time passed with

double CalcTime(timeval start, timeval end)
{
double factor = 1000000;
return (((double)end.tv_sec) * factor + ((double)end.tv_usec) - (((double)start.tv_sec) * factor + ((double)start.tv_usec))) / factor;
}

结果

结果如下图所示:

enter image description here

问题

  1. 你认为我的方法公平吗? 还是我可以避免一些不必要的管理费用?
  2. 您是否期望结果会显示 c + + 和 python 方法之间的如此巨大的差异?两者都使用共享对象进行计算。
  3. 因为我更愿意在程序中使用 python,那么在调用 BLAS 或 LAPACK 例程时,我能做些什么来提高性能呢?

下载

完整的基准可以下载 给你。(J.F. 塞巴斯蒂安使这个链接成为可能 ^ ^)

45459 次浏览

鉴于你的分析结果的严谨性,我对目前的结果感到惊讶。我把这个作为一个“答案”,但仅仅是因为它太长的评论,并提供了一种可能性(虽然我希望你已经考虑过)。

我认为 numpy/python 方法不会为一个合理复杂度的矩阵增加太多开销,因为随着复杂度的增加,python 参与的比例应该很小。我对图表右边的结果更感兴趣,但是数量级的差异会令人不安。

我想知道你是否使用了 numpy 能够利用的最好的算法:

”构建 FFTW (3.1.2) : SciPy 版本 > = 0.7和 Numpy > = 1.2: 由于许可证、配置和维护问题,在 SciPy > = 0.7和 NumPy > = 1.2的版本中删除了对 FFTW 的支持。现在使用的是内置版本的 fftpack。 如果需要进行分析,有几种方法可以利用 FFTW 的速度。 Downgrade to a Numpy/Scipy version that includes support. 安装或创建您自己的 FFTW 包装

你用 mkl 编译了 numpy 吗?(http://software.intel.com/en-us/articles/intel-mkl/).如果您在 linux 上运行,那么使用 mkl 编译 numpy 的说明如下: http://www.scipy.org/Installing_SciPy/Linux#head-7ce43956a69ec51c6f2cedd894a4715d5bfff974(尽管有 url)。关键是:

[mkl]
library_dirs = /opt/intel/composer_xe_2011_sp1.6.233/mkl/lib/intel64
include_dirs = /opt/intel/composer_xe_2011_sp1.6.233/mkl/include
mkl_libs = mkl_intel_lp64,mkl_intel_thread,mkl_core

如果你在 windows 平台上,你可以在 http://www.lfd.uci.edu/~gohlke/pythonlibs/获得一个编译好的二进制文件(还可以获得 pyfftw 和许多其他相关算法) ,感谢加州大学欧文分校荧光动力学实验室的 Christoph Gohlke。

Caveat, in either case, there are many licensing issues and so on to be aware of, but the intel page explains those. Again, I imagine you've considered this, but if you meet the licensing requirements (which on linux is very easy to do), this would speed up the numpy part a great deal relative to using a simple automatic build, without even FFTW. I'll be interested to follow this thread and see what others think. Regardless, excellent rigor and excellent question. Thanks for posting it.

下面是另一个基准测试(在 Linux 上,只需键入 make) : http://dl.dropbox.com/u/5453551/blas_call_benchmark.zip

Http://dl.dropbox.com/u/5453551/blas_call_benchmark.png

我看不出 Numpy、 Ctypes 和 Fortran 对大矩阵的不同求解方法之间有什么本质上的区别。(用 Fortran 代替 C + + ——-如果这很重要,那么您的基准测试可能已经坏了。)

Your CalcTime function in C++ seems to have a sign error. ... + ((double)start.tv_usec)) should be instead ... - ((double)start.tv_usec)). Perhaps your benchmark also has other bugs, e.g., comparing between different BLAS libraries, or different BLAS settings such as number of threads, or between real time and CPU time?

编辑 : 未能计算 CalcTime函数中的大括号——没问题。

作为指导原则: 如果你做一个基准测试,请总是张贴 所有的代码的地方。在没有完整代码的情况下对基准进行评论,尤其是在令人惊讶的情况下,通常是没有效率的。


要找出哪个 BLAS Numpy 是与之相关的,请:

$ python
Python 2.7.2+ (default, Aug 16 2011, 07:24:41)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy.core._dotblas
>>> numpy.core._dotblas.__file__
'/usr/lib/pymodules/python2.7/numpy/core/_dotblas.so'
>>>
$ ldd /usr/lib/pymodules/python2.7/numpy/core/_dotblas.so
linux-vdso.so.1 =>  (0x00007fff5ebff000)
libblas.so.3gf => /usr/lib/libblas.so.3gf (0x00007fbe618b3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbe61514000)

更新 : 如果您导入 Numpy.core. _ dotblas,则 Numpy 使用的是 BLAS 的内部备份副本,速度较慢,并且不适用于性能计算! The reply from @Woltan below indicates that this is the explanation for the difference he/she sees in Numpy vs. Ctypes+BLAS.

为了解决这个问题,您需要 ATLAS 或 MKL ——-检查以下说明: http://scipy.org/Installing_SciPy/Linux大多数 Linux 发行版都附带 ATLAS,因此最好的选择是安装它们的 libatlas-dev包(名称可能有所不同)。

我已经运行了 your benchmark。 C + + 和 numpy 在我的机器上没有什么区别:

woltan's benchmark

你认为我的方法公平吗? 还是我可以避免一些不必要的管理费用?

这似乎是公平的,因为没有结果的差异。

Would you expect that the result would show such a huge discrepancy between the c++ and python approach? Both are using shared objects for their calculations.

没有。

因为我更愿意在程序中使用 python,那么在调用 BLAS 或 LAPACK 例程时,我能做些什么来提高性能呢?

确保 numpy 在您的系统上使用 BLAS/LAPACK 库的优化版本。

更新(30.07.2014) :

I re-run the the benchmark on our new HPC. 无论是硬件还是软件栈都与原始答案中的设置相比发生了变化。

我把结果放在一个 谷歌电子表格(也包含来自原始答案的结果)。

Hardware

我们的 HPC 有两个不同的节点,一个使用 Intel Sandy Bridge CPU,另一个使用新的 Ivy Bridge CPU:

Sandy (MKL,OpenBLAS,ATLAS) :

  • CPU : 2 x 16 Intel (R) Xeon (R) E2560 Sandy Bridge@2.00 GHz (16核)
  • RAM : 64GB

Ivy (MKL,OpenBLAS,ATLAS) :

  • CPU : 2 x 20 Intel (R) Xeon (R) E2680 V2 Ivy Bridge@2.80 GHz (20核,HT = 40核)
  • RAM : 256GB

软件

The software stack is for both nodes the sam. Instead of GotoBLAS2, OpenBLAS is used and there is also a multi-threaded ATLAS BLAS that is set to 8 threads (hardcoded).

  • 操作系统 : Suse
  • Intel Compiler : ictce-5.3.0
  • Numpy: 1.8.0
  • OpenBLAS: 0.2.6
  • ATLAS:: 3.8.4

点产品基准

基准代码与下面的代码相同。然而,对于新的机器,我也运行了基准的矩阵大小 五千8000
下表包括来自原始答案的基准测试结果(重命名为: MKL —— > Nehalem MKL,Netlib BLAS —— > Nehalem Netlib BLAS 等)

Matrix multiplication (sizes=[1000,2000,3000,5000,8000])

单线程性能: single threaded performance

多线程性能(8线程) : multi-threaded (8 threads) performance

线程与矩阵大小(Ivy Bridge MKL) : Matrix-size vs threads

基准套房

benchmark suite

单线程性能: enter image description here

多线程(8线程)性能: enter image description here

结论

新的基准测试结果与原始答案中的结果类似。除了 特征值测试外,OpenBLASMKL在同一水平上执行。 特征值测试仅在 单线程模式单线程模式OpenBLAS上表现得相当好。 在多线程模式下,性能更差。

“矩阵大小与线程图”还表明,尽管 MKL 和 OpenBLAS 通常可以很好地根据核心/线程的数量进行扩展,但这取决于矩阵的大小。对于较小的矩阵,增加更多的核不会提高性能很多。

沙桥Ivy Bridge还有大约30% 的性能提升,这可能是由于更高的时钟频率(+ 0.8 Ghz)和/或更好的架构。


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

一段时间以前,我不得不优化一些线性代数计算/算法,这是写在 Python 使用 numpy 和 BLAS,所以我基准测试/测试不同的 numpy/BLAS 配置。

具体来说,我测试了:

  • ATLAS 麻木
  • Numpy with GotoBlas2 (1.13)
  • 麻木及 MKL (11.1/073)
  • Numpy 和加速框架(Mac OS X)

我确实运行了两个不同的基准:

  1. simple dot product of matrices with different sizes
  2. 基准套件,可以找到 给你

Here are my results:

机器

Linux (MKL, ATLAS, No-MKL, GotoBlas2):

  • OS : Ubuntu Lucid 10.464位。
  • CPU : 2 x 4 Intel (R) Xeon (R) E5504@2.00 GHz (8核)
  • RAM : 24GB
  • Intel Compiler : 11.1/073
  • Scipy : 0.8
  • Numpy : 1.5

Mac Book Pro (加速框架) :

  • OS: Mac OS X Snow Leopard (10.6)
  • CPU : 1 Intel Core 2 Duo 2.93 Ghz (2 Core)
  • RAM: 4 GB
  • Scipy : 0.7
  • Numpy : 1.3

Mac 服务器 (加速框架) :

  • 操作系统 : Mac OS X Snow Leopard 伺服器(10.6)
  • CPU : 4 X Intel (R) Xeon (R) E5520@2.26 Ghz (8核)
  • RAM : 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   |

Dot product benchmark - chart

基准套房

密码 :
有关基准测试套件的更多信息,请参见 给你

Results:

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 |

Benchmark suite - chart

安装

安装 MKL包括安装完整的英特尔编译器套件,这是相当直接。然而,由于一些 bug/问题,使用 MKL 支持配置和编译 numpy 有点麻烦。

GotoBlas2 是一个很小的包,可以很容易地编译为一个共享库。但是,由于 臭虫的存在,为了使用 numpy,必须在构建共享库之后重新创建它。
除了这个建设它为多个目标平台不工作,由于某些原因。因此,我必须为每个平台创建一个 。所以文件,我想有一个优化的 所以文件。

If you install numpy from Ubuntu's repository it will automatically install and configure numpy to use 地图集. Installing 地图集 from source can take some time and requires some additional steps (fortran, etc).

如果您使用 FinkMac Ports在 MacOSX 机器上安装 numpy,它会将 numpy 配置为使用 地图集苹果的加速框架。 可以通过在 _ dotblas文件上运行 ldd 或调用 Show _ config ()来检查。

结论

MKL 表现最好,其次是 GotoBlas2
特征值测试中,GotoBlas2的表现出人意料的差于预期。
Apple's Accelerate Framework performs really good especially in single threaded mode (compared to the other BLAS implementations).

GotoBlas2MKL都可以很好地扩展线程的数量。因此,如果您必须处理在多个线程上运行它的大矩阵,那么这将非常有帮助。

In any case don't use the default Netlib blas implementation because it is way too slow for any serious computational work.

On our cluster I also installed AMD's ACML and performance was similar to MKL and GotoBlas2. I don't have any numbers tough.

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

If you want to code in C++/C also check out 特征3 which is supposed to outperform MKL/GotoBlas2 in some 案件 and is also pretty easy to use.

让我为一个有点奇怪的发现做点贡献 result

My numpy is linked to mkl, as given by numpy.show_config(). I have no idea what kind of libblas.so C++/BLAS was used. I hope someone can tell me a way to figure it out.

我认为结果很大程度上取决于所使用的库,我不能孤立的 C + +/BLAS 的效率。