基准testing(使用BLAS的python与c ++)和(numpy)
我想写一个广泛使用BLAS和LAPACK线性代数function的程序。 由于performance是一个问题,我做了一些基准testing,并希望知道,如果我采取的方法是合法的。
我可以这么说,三名参赛者想用简单的matrix – matrix乘法来testing他们的performance。 参赛者是:
- Numpy,只使用
dot
的function。 - Python,通过共享对象调用BLASfunction。
- C ++,通过共享对象调用BLASfunction。
脚本
我为不同的维度i
实现了matrix – matrix乘法。 i
从5增加到500,增量为5,并且m1
和m2
的设置是这样的:
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; }
结果
结果如下图所示:
问题
- 你认为我的方法是否公平,还是有一些不必要的开销可以避免?
- 你会期望结果会显示如此巨大的c ++和python方法之间的差异? 两者都使用共享对象进行计算。
- 因为我宁愿使用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尺寸5000和8000的基准。
下表包括原始答案的基准结果(更名为:MKL – > Nehalem MKL,Netlib Blas – > Nehalem Netlib BLAS等)
单线程性能:
multithreading性能(8线程):
线程与matrix大小(Ivy Bridge MKL) :
基准套件
单线程性能:
multithreading(8线程)性能:
结论
新的基准testing结果与原来的答案类似。 除特征值testing外, OpenBLAS和MKL在同一级别上执行。 特征值testing在单线程模式下仅在OpenBLAS上performance得相当好。 在multithreading模式下,性能更差。
“matrix大小vs线程图”还显示,虽然MKL和OpenBLAS一般都可以很好地扩展核心/线程数量,但它取决于matrix的大小。 对于小型matrix来说,增加更多内核不会提高性能。
从Sandy Bridge到Ivy 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)
我确实运行了两个不同的基准:
- 不同尺寸的matrix的简单点积
- 基准套件可以在这里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实现相比)。
GotoBlas2和MKL都可以通过线程数量很好地扩展。 所以如果你必须处理在multithreading上运行的大型matrix,将会有很大的帮助。
在任何情况下都不要使用默认的netlib blas实现,因为对于任何严重的计算工作来说,这太慢了。
在我们的集群上,我还安装了AMD的ACML ,性能类似于MKL和GotoBlas2 。 我没有任何数字艰难。
我个人会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
函数似乎有一个符号错误。 也许你的基准也有其他的错误,例如,比较不同的BLAS库,或不同的BLAS设置,如线程数,或实时和CPU时间之间的差异? ... + ((double)start.tv_usec))
应该是... - ((double)start.tv_usec))
。
编辑 :无法计算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。 我会有兴趣跟随这个线索,看看别人的想法。 无论如何,优秀的严谨和优秀的问题。 感谢张贴它。