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

Wol*_*tan 105 c++ python benchmarking numpy blas

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

可以说,我有三位参赛者,他们希望用简单的矩阵矩阵乘法来测试他们的表现.参赛者是:

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

脚本

我为不同的维度实现了矩阵 - 矩阵乘法i.i为5的增量和matricies运行5-500 m1m2设置了这样的:

m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)
Run Code Online (Sandbox Code Playgroud)

Numpy

使用的代码如下所示:

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

2. Python,通过共享对象调用BLAS

有了这个功能

_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))
Run Code Online (Sandbox Code Playgroud)

测试代码如下所示:

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)))
Run Code Online (Sandbox Code Playgroud)

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

现在c ++代码自然要长一点,所以我将信息减少到最小.
我加载函数

void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
Run Code Online (Sandbox Code Playgroud)

我这样测量时间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);
Run Code Online (Sandbox Code Playgroud)

这里j是运行20次的循环.我计算过去的时间

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;
}
Run Code Online (Sandbox Code Playgroud)

结果

结果如下图所示:

在此输入图像描述

问题

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

下载

完整的基准测试可以在这里下载.(JF塞巴斯蒂安使该链接成为可能^^)

Ümi*_*mit 72

更新(2014年7月30日):

我在新的HPC上重新运行基准测试.硬件和软件堆栈都改变了原始答案中的设置.

我将结果放在谷歌电子表格中(也包含原始答案的结果).

硬件

我们的HPC有两个不同的节点,一个是Intel Sandy Bridge CPU,另一个是新的Iv​​y Bridge CPU:

桑迪(MKL,OpenBLAS,ATLAS):

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

常春藤(MKL,OpenBLAS,ATLAS):

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

软件

软件堆栈适用于两个节点sam.代替GotoBLAS2,OpenBLAS被使用并且也有一个多线程的ATLAS它被设置为8个线程(硬编码)BLAS.

  • 操作系统:Suse
  • 英特尔编译器:ictce-5.3.0
  • Numpy: 1.8.0
  • OpenBLAS: 0.2.6
  • 阿特拉斯: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上的表现相当不错.在多线程模式下,性能更差.

"矩阵大小VS线程图表"也表明,虽然MKL以及OpenBLAS通常与核/线程的数量很好地扩展,这取决于基质的大小.对于小型矩阵,添加更多内核不会极大地提高性能.

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


原答案(04.10.2011):

前段时间我不得不优化一些使用numpy和BLAS用python编写的线性代数计算/算法,所以我对不同的numpy/BLAS配置进行了基准测试.

特别是我测试过:

  • Numpy与ATLAS
  • Numpy与GotoBlas2(1.13)
  • Numpy with MKL(11.1/073)
  • Numpy with Accelerate Framework(Mac OS X)

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

  1. 不同大小的矩阵的简单点积
  2. 基准套房,可在这里找到.

这是我的结果:

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

  • 操作系统:Ubuntu Lucid 10.4 64位.
  • CPU:2 x 4英特尔(R)Xeon(R)E5504 @ 2.00GHz(8核)
  • RAM:24 GB
  • 英特尔编译器:11.1/073
  • Scipy:0.8
  • Numpy:1.5

Mac Book Pro(加速框架):

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

Mac服务器(加速框架):

  • 操作系统:Mac OS X Snow Leopard Server(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)
Run Code Online (Sandbox Code Playgroud)

结果:

    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等).

如果您在具有FinkMac端口的Mac OS X计算机上安装numpy,它将配置numpy以使用ATLASApple的Accelerate Framework.您可以通过在numpy.core._dotblas文件上运行ldd 或调用numpy.show_config()来进行检查.

结论

MKL表现最好,其次是GotoBlas2.
特征值测试中,GotoBlas2的表现出乎意料地差于预期.不知道为什么会这样.
Apple的Accelerate Framework在单线程模式下表现非常好(与其他BLAS实现相比).

GotoBlas2MKL都可以很好地扩展线程数.因此,如果你必须处理在多个线程上运行它的大矩阵将有很大帮助.

在任何情况下都不要使用默认的netlib blas实现,因为对于任何严肃的计算工作来说它太慢了.

在我们的集群上,我还安装了AMD的ACML,其性能类似于MKLGotoBlas2.我没有任何强硬的数字.

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

如果你想在C++/C中编码也可以查看Eigen3,它在某些情况下应该胜过MKL/GotoBlas2,并且也很容易使用.

  • 抱歉我有点困惑。最初的基准测试似乎是将 C++ 性能与 Numpy 进行比较,但这仅适用于具有不同 linalg 库和架构的 Numpy。我错过了什么吗? (2认同)

jfs*_*jfs 55

我碰到你的标杆.我的机器上的C++和numpy没有区别:

沃尔坦的基准

你认为我的方法是公平的,还是有一些我可以避免的不必要的开销?

由于结果没有差异,这似乎是公平的.

您是否期望结果会在c ++和python方法之间显示出如此巨大的差异?两者都使用共享对象进行计算.

没有.

因为我宁愿在我的程序中使用python,所以在调用BLAS或LAPACK例程时如何提高性能呢?

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

  • 那么原始海报做错了什么?我希望他对这篇文章发表评论.他是否确认Numpy和C++一样快? (3认同)

pv.*_*pv. 20

这是另一个基准测试(在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++ ---如果这很重要,你的基准测试可能会被打破.)

CalcTime在C++中的函数似乎有一个符号错误.... + ((double)start.tv_usec))应该是... - ((double)start.tv_usec)).也许您的基准测试还有其他错误,例如,比较不同的BLAS库,或不同的BLAS设置,如线程数,或实时和CPU时间之间?

编辑:未能计算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内部后备副本,这个副本速度较慢,并不打算用于性能计算!来自@Woltan的回复表明,这是他/她在Numpy vs. Ctypes + BLAS中看到的差异的解释.

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

  • @Woltan:您无法导入numpy.core._dotblas这一事实意味着您的Numpy正在使用其BLAS的内部备用副本(*较慢*,而不是用于性能计算!),而不是BLAS库你有你的系统.这解释了您从基准测试中获得的结果.要解决这个问题,你需要安装Numpy可以使用的BLAS版本---这意味着ATLAS或MKL.这是一组说明:http://scipy.org/Installing_SciPy/Linux (2认同)

Pro*_*ane 9

鉴于您在分析中表现出的严谨性,我对迄今为止的结果感到惊讶.我把它作为一个"答案",但这只是因为评论太长了并且提供了一种可能性(尽管我希望你已经考虑过了).

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

我想知道你是否正在使用numpy可以利用的最佳算法.从linux的编译指南:

"构建FFTW(3.1.2):SciPy版本> = 0.7且Numpy> = 1.2:由于许可证,配置和维护问题,在SciPy> = 0.7和NumPy> = 1.2的版本中删除了对FFTW的支持. fftpack的内置版本.如果需要分析,有几种方法可以利用FFTW的速度.降级到包含支持的Numpy/Scipy版本.安装或创建自己的FFTW包装.请参阅http: //developer.berlios.de/projects/pyfftw/作为一个未得到认可的例子."

你用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 
Run Code Online (Sandbox Code Playgroud)

如果您使用的是Windows,你可以在获得与MKL已编译的二进制,(也获得pyfftw,和许多其他相关算法):http://www.lfd.uci.edu/~gohlke/pythonlibs/,用感谢加州大学欧文分校荧光动力学实验室的Christoph Gohlke.

在任何一种情况下,都有许多许可问题需要注意,但英特尔页面解释了这些问题.再说一遍,我想你已经考虑过了,但是如果你满足了许可要求(在linux上很容易做到),相对于使用简单的自动构建而言,这甚至可以加速numpy部分,甚至没有FFTW.我有兴趣关注这个帖子,看看别人怎么想.无论如何,优秀的严谨和优秀的问题.谢谢你发布它.