基准测试(python vs. c++ using BLAS)和(numpy)

Posted

技术标签:

【中文标题】基准测试(python vs. c++ using BLAS)和(numpy)【英文标题】:Benchmarking (python vs. c++ using BLAS) and (numpy) 【发布时间】:2011-11-27 15:18:49 【问题描述】:

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

可以这么说,我有三个参赛者,想用简单的矩阵-矩阵乘法来测试他们的表现。参赛选手是:

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

场景

我为不同维度实现了矩阵-矩阵乘法ii 从 5 运行到 500,增量为 5,矩阵 m1m2 设置如下:

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

1。麻木

使用的代码如下所示:

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

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

测试代码如下所示:

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

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 例程时我可以做些什么来提高性能?

下载

完整的基准测试可以下载here。 (J.F. Sebastian 使该链接成为可能^^)

【问题讨论】:

在您的 ctypes 方法中,您在测量函数内部分配了内存。您的 c++ 代码是否遵循这种方法?但是与矩阵乘法相比,这应该没有太大区别...... @rocksportrocker 你是对的。 r 矩阵的内存分配是不公平的。我现在正在解决“问题”并发布新结果。 1.确保数组具有相同的内存布局np.ascontiguousarray()(考虑 C 与 Fortran 顺序)。 2.确保np.dot()使用相同的libblas.so @Woltan:不要使用 filefactory 服务太糟糕了。我已将您的基准添加到 github:woltan-benchmark。如果您使用 github,我可以将您添加为合作者。 我跑了your benchmarks under Python 3,得到了similar discrepancy between Python and C++。虽然我知道 numpy 的性能取决于它链接的 blas 库,但我不知道为什么 Python/BLAS 和 C++/BLAS 之间存在差异,因为它们都链接到同一个库。你是如何解决这个问题的? 【参考方案1】:

更新(2014 年 7 月 30 日):

我在我们的新 HPC 上重新运行基准测试。 硬件和软件堆栈都与原始答案中的设置有所不同。

我将结果放在google spreadsheet 中(还包含原始答案的结果)。

硬件

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

桑迪(MKL、OpenBLAS、ATLAS):

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

常春藤(MKL、OpenBLAS、ATLAS):

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

软件

软件堆栈适用于 sam 的两个节点。代替 GotoBLAS2,使用 OpenBLAS,还有一个设置为 8 个线程(硬编码)的 多线程 ATLAS 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 等)

单线程性能:

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

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

基准套件

单线程性能:

多线程(8 线程)性能:

结论

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

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

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


原始答案 (04.10.2011):

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

我具体测试过:

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

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

    不同大小矩阵的简单点积 可以在here找到的基准套件。

这是我的结果:

机器

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

操作系统:Ubuntu Lucid 10.4 64 位。 CPU:2 x 4 Intel(R) Xeon(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 Snow Leopard Server (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)

结果

系统 |大小 = 1000 |大小 = 2000 |大小 = 3000 | netlib BLAS | 1350 毫秒 | 10900 毫秒 | 39200 毫秒 | ATLAS (1 CPU) | 314 毫秒 | 2560 毫秒 | 8700 毫秒 | MKL(1 个 CPU)| 268 毫秒 | 2110 毫秒 | 7120 毫秒 | MKL(2 个 CPU)| - | - | 3660 毫秒 | MKL(8 个 CPU)| 39 毫秒 | 319 毫秒 | 1000 毫秒 | GotoBlas2 (1 CPU) | 266 毫秒 | 2100 毫秒 | 7280 毫秒 | GotoBlas2(2 个 CPU)| 139 毫秒 | 1009 毫秒 | 3690 毫秒 | GotoBlas2(8 个 CPU)| 54 毫秒 | 389 毫秒 | 1250 毫秒 | Mac OS X (1 CPU) | 143 毫秒 | 1060 毫秒 | 3605 毫秒 | Mac 服务器(1 个 CPU)| 92 毫秒 | 714 毫秒 | 2130 毫秒 |

基准套件

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

结果

系统 |特征值 | svd |检测 |库存 |点 | netlib BLAS | 1688 毫秒 | 13102 毫秒 | 438 毫秒 | 2155 毫秒 | 3522 毫秒 | ATLAS (1 CPU) | 1210 毫秒 | 5897 毫秒 | 170 毫秒 | 560 毫秒 | 893 毫秒 | MKL(1 个 CPU)| 691 毫秒 | 4475 毫秒 | 141 毫秒 | 450 毫秒 | 736 毫秒 | MKL(2 个 CPU)| 552 毫秒 | 2718 毫秒 | 96 毫秒 | 267 毫秒 | 423 毫秒 | MKL(8 个 CPU)| 525 毫秒 | 1679 毫秒 | 60 毫秒 | 137 毫秒 | 197 毫秒 | GotoBlas2 (1 CPU) | 2124 毫秒 | 4636 毫秒 | 147 毫秒 | 456 毫秒 | 743 毫秒 | GotoBlas2(2 个 CPU)| 1560 毫秒 | 3278 毫秒 | 116 毫秒 | 295 毫秒 | 460 毫秒 | GotoBlas2(8 个 CPU)| 741 毫秒 | 2914 毫秒 | 82 毫秒 | 262 毫秒 | 192 毫秒 | Mac OS X (1 CPU) | 948 毫秒 | 4339 毫秒 | 151 毫秒 | 318 毫秒 | 566 毫秒 | Mac 服务器(1 个 CPU)| 1033 毫秒 | 3645 毫秒 | 99 毫秒 | 232 毫秒 | 342 毫秒 |

安装

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

GotoBlas2 是一个可以轻松编译为共享库的小包。但是,由于bug,您必须在构建共享库后重新创建它才能与 numpy 一起使用它。 除了为多个目标平台构建它之外,由于某种原因,它无法正常工作。所以我必须为每个平台创建一个 .so 文件,我希望有一个优化的 libgoto2.so 文件。

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

如果您在带有 FinkMac Ports 的 Mac OS X 机器上安装 numpy,它会将 numpy 配置为使用 ATLASApple 的加速框架。 您可以通过在 numpy.core._dotblas 文件上运行 ldd 或调用 numpy.show_config() 来检查。

结论

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

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

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

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

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

如果您想用 C++/C 编写代码,还请查看 Eigen3,它在某些 cases 中的性能应该优于 MKL/GotoBlas2,并且也非常易于使用。

【讨论】:

非常感谢您的详尽回答! 很全面,谢谢!我想知道,三年后,OpenBLAS(据我所知,它是 GotoBLAS 的后代)是否会表现得更好。我在某处读到它的性能优于 MKL,但现在找不到源代码。 谢谢!这是我对 0 的印象(我想知道这是否只是我的安装):OpenBLAS 在多线程模式下在对角化矩阵时表现不佳(我在 scipy 中对角化,它与 OpenBLAS 相关联)。 @William:通常你不需要专门将 scipy 链接到 openblas,因为它会在安装过程中使用 numpy 配置,实际上大多数 BLAS/Lapack 调用都会被转发到 numpy。因此,如果 numpy 与 openblas 正确链接,一切都应该正常。 对不起,我有点困惑。最初的基准测试似乎将 C++ 性能与 Numpy 进行了比较,但是,这仅适用于具有不同 linalg 库和架构的 Numpy。我错过了什么吗?【参考方案2】:

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

您认为我的方法是否公平,或者我可以避免一些不必要的开销?

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

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

没有。

由于我宁愿在我的程序中使用 python,在调用 BLAS 或 LAPACK 例程时我可以做些什么来提高性能?

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

【讨论】:

那么原贴做错了什么?我希望他对这篇文章发表了评论。他是否确认 Numpy 与 C++ 一样快? 您的 C++ 代码运行速度比原始海报慢。你是在优化下编译的吗? @cdcdcd 这不是我的代码。单击链接并使用不同的优化选项自行运行基准测试(请参阅 Makefile)。虽然代码不会重新编译 blas 和 lapack。【参考方案3】:

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

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

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

作为指导:如果您进行基准测试,请始终将代码发布在某处所有。在没有完整代码的情况下对基准进行评论,尤其是在令人惊讶的情况下,通常是没有效率的。


要找出链接到哪个 BLAS Numpy,请执行以下操作:

$蟒蛇 Python 2.7.2+(默认,2011 年 8 月 16 日,07:24:41) [GCC 4.6.1] 在 linux2 上 输入“帮助”、“版权”、“信用”或“许可”以获取更多信息。 >>> 导入 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 与 Ctypes+BLAS 中看到的差异的解释。

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

【讨论】:

我已经运行了你的基准测试;结果是the same 非常感谢您的发帖。我用this 结果运行了你的基准测试。所以我不能复制你的。要检查我的 numpy 正在使用哪个 BLAS:我不能 import numpy.core._dotblas。这里可能是什么问题?我将尝试清理我的基准测试并编写一个 makefile 以便其他人对其进行测试。 @Woltan:您无法导入 numpy.core._dotblas 的事实意味着您的 Numpy 正在使用其内部的 BLAS 后备副本(较慢,并不意味着用于性能计算!),而不是您系统上的 BLAS 库。这解释了您从基准测试中获得的结果。要解决这种情况,您需要安装 Numpy 可以使用的 BLAS 版本 --- 这意味着 ATLAS 或 MKL。这是一组说明:scipy.org/Installing_SciPy/Linux @pv.: 你能运行Woltan's benchmark 来比较结果吗? 在 Mac 上,您可以在 Linux 上使用 otool -L 而不是 ldd【参考方案4】:

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

我原以为 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 

如果您在 Windows 上,您可以使用 mkl 获得编译后的二进制文件,(还可以获得 pyfftw 和许多其他相关算法),地址为:http://www.lfd.uci.edu/~gohlke/pythonlibs/,感谢荧光实验室的 Christoph Gohlke动力学,加州大学尔湾分校。

注意,无论哪种情况,都需要注意许多许可问题等,但英特尔页面解释了这些问题。同样,我想您已经考虑过这一点,但是如果您满足许可要求(在 linux 上很容易做到),与使用简单的自动构建相比,这将大大加快 numpy 部分,甚至没有 FFTW。我有兴趣关注这个帖子,看看其他人的想法。无论如何,非常严谨和出色的问题。感谢您发布它。

【讨论】:

感谢您精心的“评论”^^。为了澄清我的 python/numpy/BLAS 设置:我遵循this 安装指南。我在一个 linux 操作系统上,版本是:Python 2.7,Scipy 0.9 Numpy 1.6。不幸的是,我没有事先构建 FFTW,也没有使用 mkl... 在某种程度上,这是幸运的。这意味着python结果有很大的改进空间,听起来你想使用python。我认为,如果您将构建修改为链接上显示的构建,您会对 numpy 的速度感到更加满意,尽管我仍然很想看看它与您的 C++ 实现相比如何。 您也可以尝试构建 ATLAS,但这听起来对我的性能需求来说太令人头疼了,所以我没有任何经验。我想如果您对使用 python 感兴趣但能够使用 C++,那么在某些时候进行大量特殊编译的设置成本会超过语言节省,并且使用 C++ 会更容易。但是 mkl 和 fftw 都应该很简单。 目前 MKL、Accelerate 和 OpenBLAS 在性能上是相似的。不过,OpenBLAS 比 MKL 更具可扩展性。【参考方案5】:

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

我的numpy 链接到mkl,由 numpy.show_config() 给出。我不知道使用了哪种libblas.so C++/BLAS。我希望有人能告诉我一个解决办法。

我认为结果很大程度上取决于所使用的库。我无法隔离 C++/BLAS 的效率。

【讨论】:

以上是关于基准测试(python vs. c++ using BLAS)和(numpy)的主要内容,如果未能解决你的问题,请参考以下文章

siege vs wrk的负载测试和基准测试[关闭]

Web 服务器基准测试,nginx+php vs Apache+php

Oracle 11g vs MySQL vs Hadoop:- 10^6 到 10^9 记录的基准测试

使用 Nbody 测试对 GCC 7 与 VS2017 进行基准测试

C++ 中的 HDD 基准测试 - 测量的传输速度太快

C++开发python windows版本的扩展模块示例