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

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

我可以这么说,三名参赛者想用简单的matrix – matrix乘法来testing他们的performance。 参赛者是:

  1. Numpy,只使用dot的function。
  2. Python,通过共享对象调用BLASfunction。
  3. C ++,通过共享对象调用BLASfunction。

脚本

我为不同的维度i实现了matrix – matrix乘法。 i从5增加到500,增量为5,并且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,通过共享对象调用BLAS

有了这个function

 _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)) 

testing代码如下所示:

 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 ++代码自然是更长一点,所以我把信息减less到最低限度。
我加载的function

 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); 

其中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; } 

结果

结果如下图所示:

在这里输入图像说明

问题

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

下载

完整的基准可以在这里下载。 (JF塞巴斯蒂安提出可能的链接^^)

我已经运行你的基准 。 在我的机器上C ++和numpy没有区别:

沃尔坦的基准

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

这似乎是公平的,因为结果没有什么不同。

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

没有。

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

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

更新(30.07.2014):

我重新运行我们新的HPC的基准。 硬件以及软件堆栈从原始答案中的设置改变了。

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

硬件

我们的HPC有两个不同的节点,一个是英特尔Sandy Bridge CPU,另一个是较新的Ivy Bridge CPU:

桑迪 (MKL,OpenBLAS,ATLAS):

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

常春藤 (MKL,OpenBLAS,ATLAS):

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

软件

软件堆栈是两个节点的山姆。 使用OpenBLAS而不是GotoBLAS2 ,并且还有一个multithreading的ATLAS BLAS被设置为8个线程(硬编码)。

  • OS :Suse
  • 英特尔编译器 :ictce-5.3.0
  • Numpy: 1.8.0
  • OpenBLAS: 0.2.6
  • ATLAS :3.8.4

点积产品基准

基准代码与以下相同。 但是,对于新机器,我还运行了matrix尺寸50008000的基准。
下表包括原始答案的基准结果(更名为:MKL – > Nehalem MKL,Netlib Blas – > Nehalem Netlib BLAS等)

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

单线程性能: 单线程性能

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

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

基准套件

基准套件

单线程性能: 在这里输入图像说明

multithreading(8线程)性能: 在这里输入图像说明

结论

新的基准testing结果与原来的答案类似。 除特征值testing外, OpenBLASMKL在同一级别上执行。 特征值testing在单线程模式下仅在OpenBLAS上performance得相当好。 在multithreading模式下,性能更差。

“matrix大小vs线程图”还显示,虽然MKL和OpenBLAS一般都可以很好地扩展核心/线程数量,但它取决于matrix的大小。 对于小型matrix来说,增加更多内核不会提高性能。

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


原答复(04.10.2011):

前一段时间,我不得不优化一些线性代数计算/algorithm,这些algorithm是使用numpy和BLAS编写的,所以我testing了不同的numpy / BLASconfiguration。

具体我testing了:

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

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

  1. 不同尺寸的matrix的简单点积
  2. 基准套件可以在这里find。

这是我的结果:

Linux (MKL,ATLAS,No-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个Intel Core 2 Duo 2.93 Ghz(2核)
  • 内存 :4 GB
  • Scipy :0.7
  • Numpy :1.3

Mac服务器 (加速框架):

  • 操作系统 :Mac OS X雪豹服务器(10.6)
  • CPU :4 X Intel(R)Xeon(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) 

结果

    系统|  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个CPU)|  268 ms |  2110 ms |  7120 ms |
 MKL(2个CPU)|  -  |  -  |  3660 ms |
 MKL(8个CPU)|  39 ms |  319 ms |  1000 ms |
 GotoBlas2(1 CPU)|  266 ms |  2100 ms |  7280 ms |
 GotoBlas2(2个CPU)|  139 ms |  1009 ms |  3690 ms |
 GotoBlas2(8个CPU)|  54 ms |  389 ms |  1250 ms |
 Mac OS X(1个CPU)|  143 ms |  1060 ms |  3605 ms |
 Mac服务器(1个CPU)|  92 ms |  714 ms |  2130 ms |

点产品基准 - 图表

基准套件

代码
有关基准套件的更多信息,请参阅此处 。

结果

    系统| 特征值|  svd |  det |  inv | 点|
 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个CPU)|  691 ms |  4475 ms |  141 ms |  450 ms |  736 ms |
 MKL(2个CPU)|  552 ms |  2718 ms |  96 ms |  267 ms |  423 ms |
 MKL(8个CPU)|  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个CPU)|  1560 ms |  3278 ms |  116 ms |  295 ms |  460 ms |
 GotoBlas2(8个CPU)|  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服务器(1个CPU)|  1033 ms |  3645 ms |  99 ms |  232 ms |  342 ms |

基准套件 - 图表

安装

安装MKL包括安装完整的英特尔编译器套件,这非常简单。 然而,由于一些错误/问题configuration和编译与MKL支持numpy有点麻烦。

GotoBlas2是一个小软件包,可以很容易地编译为共享库。 然而,由于一个bug,你必须在创build共享库之后重新创build共享库才能使用numpy。
除了这个build筑物,它为多个目标plattform没有工作的原因。 所以我不得不为每个平台创build一个.so文件,我想要一个优化的libgoto2.so文件。

如果您从Ubuntu的存储库安装numpy,它会自动安装并configurationnumpy以使用ATLAS 。 从源代码安装ATLAS可能需要一些时间,并需要一些额外的步骤(fortran等)。

如果你在Mac OS X机器上安装numpy,使用Fink或者Mac Ports,它可以configurationnumpy使用ATLAS或者Apple的Accelerate Framework 。 您可以通过在numpy.core._dotblas文件上运行ldd或调用numpy.show_config()来检查

结论

MKL的执行情况最好紧随GotoBlas2
特征值testing中,GotoBlas2的performance比预期的要差得多。 不知道为什么这样。
苹果公司的Accelerate Framework在单线程模式下性能非常好(与其他BLAS实现相比)。

GotoBlas2MKL可以通过线程数量很好地扩展。 所以如果你必须处理在multithreading上运行的大型matrix,将会有很大的帮助。

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

在我们的集群上,我还安装了AMD的ACML ,性能类似于MKLGotoBlas2 。 我没有任何数字艰难。

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

如果你想用C ++ / C编写代码,在一些情况下 ,也可以查看Eigen3 ,它的性能要优于MKL / GotoBlas2 ,而且使用起来也相当简单。

这是另一个基准testing(在Linux上,只需inputmake ): http : //dl.dropbox.com/u/5453551/blas_call_benchmark.zip

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

在Numpy,Ctypes和Fortran之间,我不认为大matrix的不同方法之间有什么区别。 (Fortran而不是C ++ —如果这很重要,那么你的基准可能会被破坏。)

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

编辑 :无法计算CalcTime函数中的大括号 – 没关系。

作为一个指导原则:如果你做一个基准testing,请始终将所有的代码发布到某个地方。 评论基准,特别是当出乎意料的时候,没有完整的代码通常是没有生产力的。


要找出哪个BLAS Numpy与之链接,请执行:

 $ python
 Python 2.7.2+(默认,2011年8月16日,07:24:41) 
 linux2上的[GCC 4.6.1]
input“帮助”,“版权”,“学分”或“许可证”以获取更多信息。
 >>> 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 : libatlas-dev大多数Linux发行版都附带了ATLAS,所以最好的select是安装libatlas-dev软件包(名称可能会有所不同)。

鉴于您在分析中所performance的严谨性,迄今为止的结果让我感到惊讶。 我把这个作为一个“答案”,但只是因为它太长的评论,并提供了一个可能性(虽然我希望你已经考虑过了)。

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

我想知道你是否正在使用numpy可以利用的最好的algorithm。 从linux编译指南:

“构buildFFTW(3.1.2):SciPy版本> = 0.7,Numpy> = 1.2:由于许可证,configuration和维护问题,在SciPy> = 0.7和NumPy> = 1.2版本中,一个内置版本的fftpack。有两种方法可以利用FFTW的速度来进行分析。降级到Numpy / Scipy版本,包括支持。安装或创build你自己的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 (尽pipeurl)。 关键部分是:

 [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/用mkl获得一个编译后的二进制文件(也可以获得pyfftw和许多其他的相关algorithm)感谢克里斯托弗Gohlke荧光动力学实验室,加州大学欧文分校。;

注意,无论哪种情况,都有许多许可问题需要了解,但英特尔网页解释了这些问题。 再一次,我想你已经考虑过了,但是如果你符合许可要求(在linux上很容易做到这一点),那么相对于使用简单的自动构build而言,这将会加快numpy部分,甚至不需要FFTW。 我会有兴趣跟随这个线索,看看别人的想法。 无论如何,优秀的严谨和优秀的问题。 感谢张贴它。