比较 blitz++、犰狳、boost::MultiArray
Posted
技术标签:
【中文标题】比较 blitz++、犰狳、boost::MultiArray【英文标题】:Compare blitz++, armadillo, boost::MultiArray 【发布时间】:2013-01-03 02:33:48 【问题描述】:我用下面的代码(借用自an old post)对blitz++、armadillo、boost::MultiArray进行了比较
#include <iostream>
using namespace std;
#include <windows.h>
#define _SCL_SECURE_NO_WARNINGS
#define BOOST_DISABLE_ASSERTS
#include <boost/multi_array.hpp>
#include <blitz/array.h>
#include <armadillo>
int main(int argc, char* argv[])
const int X_SIZE = 1000;
const int Y_SIZE = 1000;
const int ITERATIONS = 100;
unsigned int startTime = 0;
unsigned int endTime = 0;
// Create the boost array
//------------------Measure boost Loop------------------------------------------
typedef boost::multi_array<double, 2> ImageArrayType;
ImageArrayType boostMatrix(boost::extents[X_SIZE][Y_SIZE]);
startTime = ::GetTickCount();
for (int i = 0; i < ITERATIONS; ++i)
for (int x = 0; x < X_SIZE; ++x)
for (int y = 0; y < Y_SIZE; ++y)
boostMatrix[x][y] = 1.0001;
endTime = ::GetTickCount();
printf("[Boost Loop] Elapsed time: %6.3f seconds\n", (endTime - startTime) / 1000.0);
//------------------Measure blitz Loop-------------------------------------------
blitz::Array<double, 2> blitzArray( X_SIZE, Y_SIZE );
startTime = ::GetTickCount();
for (int i = 0; i < ITERATIONS; ++i)
for (int x = 0; x < X_SIZE; ++x)
for (int y = 0; y < Y_SIZE; ++y)
blitzArray(x,y) = 1.0001;
endTime = ::GetTickCount();
printf("[Blitz Loop] Elapsed time: %6.3f seconds\n", (endTime - startTime) / 1000.0);
//------------------Measure armadillo loop----------------------------------------
arma::mat matArray( X_SIZE, Y_SIZE );
startTime = ::GetTickCount();
for (int i = 0; i < ITERATIONS; ++i)
for (int y = 0; y < Y_SIZE; ++y)
for (int x = 0; x < X_SIZE; ++x)
matArray(x,y) = 1.0001;
endTime = ::GetTickCount();
printf("[arma Loop] Elapsed time: %6.3f seconds\n", (endTime - startTime) / 1000.0);
//------------------Measure native loop----------------------------------------
// Create the native array
double *nativeMatrix = new double [X_SIZE * Y_SIZE];
startTime = ::GetTickCount();
for (int i = 0; i < ITERATIONS; ++i)
for (int y = 0; y < Y_SIZE*X_SIZE; ++y)
nativeMatrix[y] = 1.0001;
endTime = ::GetTickCount();
printf("[Native Loop]Elapsed time: %6.3f seconds\n", (endTime - startTime) / 1000.0);
delete[] nativeMatrix;
//------------------Measure boost computation-----------------------------------
typedef boost::multi_array<double, 2> ImageArrayType;
ImageArrayType boostMatrix(boost::extents[X_SIZE][Y_SIZE]);
for (int x = 0; x < X_SIZE; ++x)
for (int y = 0; y < Y_SIZE; ++y)
boostMatrix[x][y] = 1.0001;
startTime = ::GetTickCount();
for (int i = 0; i < ITERATIONS; ++i)
for (int x = 0; x < X_SIZE; ++x)
for (int y = 0; y < Y_SIZE; ++y)
boostMatrix[x][y] += boostMatrix[x][y] * 0.5;
endTime = ::GetTickCount();
printf("[Boost computation] Elapsed time: %6.3f seconds\n", (endTime - startTime) / 1000.0);
//------------------Measure blitz computation-----------------------------------
blitz::Array<double, 2> blitzArray( X_SIZE, Y_SIZE );
blitzArray = 1.0001;
startTime = ::GetTickCount();
for (int i = 0; i < ITERATIONS; ++i)
blitzArray += blitzArray*0.5;
endTime = ::GetTickCount();
printf("[Blitz computation] Elapsed time: %6.3f seconds\n", (endTime - startTime) / 1000.0);
//------------------Measure armadillo computation-------------------------------
arma::mat matArray( X_SIZE, Y_SIZE );
matArray.fill(1.0001);
startTime = ::GetTickCount();
for (int i = 0; i < ITERATIONS; ++i)
//matArray.fill(1.0001);
matArray += matArray*0.5;
endTime = ::GetTickCount();
printf("[arma computation] Elapsed time: %6.3f seconds\n", (endTime - startTime) / 1000.0);
//------------------Measure native computation------------------------------------------
// Create the native array
double *nativeMatrix = new double [X_SIZE * Y_SIZE];
for (int y = 0; y < Y_SIZE*X_SIZE; ++y)
nativeMatrix[y] = 1.0001;
startTime = ::GetTickCount();
for (int i = 0; i < ITERATIONS; ++i)
for (int y = 0; y < Y_SIZE*X_SIZE; ++y)
nativeMatrix[y] += nativeMatrix[y] * 0.5;
endTime = ::GetTickCount();
printf("[Native computation]Elapsed time: %6.3f seconds\n", (endTime - startTime) / 1000.0);
delete[] nativeMatrix;
return 0;
在windows,VS2010上,结果是
[Boost Loop] Elapsed time: 1.217 seconds
[Blitz Loop] Elapsed time: 0.046 seconds
[arma Loop] Elapsed time: 0.078 seconds
[Native Loop]Elapsed time: 0.172 seconds
[Boost computation] Elapsed time: 2.152 seconds
[Blitz computation] Elapsed time: 0.156 seconds
[arma computation] Elapsed time: 0.078 seconds
[Native computation]Elapsed time: 0.078 seconds
在windows,intel c++上,结果是
[Boost Loop] Elapsed time: 0.468 seconds
[Blitz Loop] Elapsed time: 0.125 seconds
[arma Loop] Elapsed time: 0.046 seconds
[Native Loop]Elapsed time: 0.047 seconds
[Boost computation] Elapsed time: 0.796 seconds
[Blitz computation] Elapsed time: 0.109 seconds
[arma computation] Elapsed time: 0.078 seconds
[Native computation]Elapsed time: 0.062 seconds
有些奇怪:
(1) with VS2010, native computation (including loop) is faster than native loop
(2) blitz loop behave so different under VS2010 and intel C++.
要使用 intel c++ 编译器编译 blitz++,blitz/intel/ 文件夹中需要一个名为 bzconfig.h 的文件。但是没有。我只是复制 blitz/ms/bzconfig.h 中的那个。这可能会给出非最佳配置。任何人都可以告诉我如何使用 intel c++ 编译器编译 blitz++?在手册中,它说运行 bzconfig 脚本以获取正确的 bzconfig.h。但我不明白这是什么意思。
非常感谢!
添加一些我的结论:
1. Boost multi array is the slowest.
2. With intel c++ compiler, native pointers are very fast.
3. With intel c++ compiler, armadillo can achieve the performance of native pointers.
4. Also test eigen, it is x0% slower than armadillo in my simple cases.
5. Curious about blitz++'s behavior in intel c++ compiler with proper configuration.
Please see my question.
【问题讨论】:
请注意,默认情况下Armadillo 已启用边界检查(以及其他有用的健全性检查)。原因是首先让你的算法正确,然后优化它。通过禁用边界检查,您可以从 Armadillo 获得更快的速度,如 here 和 here 所述。基本上它相当于在包含犰狳头之前定义 ARMA_NO_DEBUG,与 BOOST_DISABLE_ASSERTS 类似。 你是如何尝试更改顺序的,所以它显示为:native、arma、blitz、boost。 那么,你唯一的问题是“谁能告诉我如何用intel c++编译器编译blitz++?”。如果是这样,请添加详细信息,我们不需要其余的 - 它属于博客文章,或者如果您正在征求反馈意见,可能在 codereview.stackexchange.com 上。 我投票决定将此问题作为离题结束,因为它寻求构建帮助,并且类似于对调试帮助的期望,我希望得到一个最小而清晰的问题陈述。事实上,问题隐藏在不相关的代码和观察中。 其中许多帖子,包括您引用的帖子,都因无法比较喜欢与喜欢而受苦。对您引用的帖子***.com/questions/446866/… 的最新回复表明,对于针对 boost 优化的比较,可以使 multi_array 与本机数组实现相提并论。 【参考方案1】:简答:./configure CXX=icpc
,通过阅读 Blitz++ 用户指南找到。
长答案:
要使用 intel c++ 编译器编译 blitz++,blitz/intel/ 文件夹中需要一个名为 bzconfig.h 的文件。但是没有。
是的,是的。 Blitz++ 应该自己生成文件。根据 blitz-0.10.tar.gz
中包含的 Blitz++ 用户指南 blitz.pdf
,“安装”部分,
Blitz++ 使用 GNU Autoconf,它为各种平台和编译器处理重写 Makefile。
更准确地说,Blitz++ 使用 GNU autotools 工具链(automake、autoconf、configure),可以生成 makefile、配置脚本、头文件等。 bzconfig.h
文件应该是由 Blitz++ 自带的 configure
脚本生成的,可以直接使用。
我只是将 blitz/ms/bzconfig.h 中的那个复制进去。这可能会给出非最佳配置。
如果“非最佳”对您来说意味着“不工作”,那么是的。 :-)
您需要一个准确代表您的编译器的intel/bzconfig.h
。
谁能告诉我如何用intel c++编译器编译blitz++?
阅读并遵循精美的手册,尤其是上面提到的“安装”部分。
进入“blitz-VERSION”目录,然后输入:
./configure CXX=[compiler]
其中 [compiler] 是 xlc++、icpc、pathCC、xlC、cxx、aCC、CC、g++、KCC、pgCC 或 FCC 之一。 (如果不选择 C++ 编译器,configure 脚本会尝试为当前平台寻找合适的编译器。)
你做过吗?对于英特尔编译器,您需要使用
./configure CXX=icpc
.
在手册中,它说运行 bzconfig 脚本以获取正确的 bzconfig.h。但我不明白这是什么意思。
我假设“它”是指“那个”。 “手动”是什么意思?我的 Blitz++ 用户指南副本没有提到 bzconfig
。您确定您使用的是与您的 Blitz++ 版本对应的手册吗?
PS:在blitz-0.10的内容中寻找“bzconfig”,看起来“bzconfig”不再是Blitz++的一部分,而是曾经是:
find . -name bzconfig
-> 没有结果
find . -print0 | xargs -0 grep -a -i -n -e bzconfig
:
./blitz/compiler.h:44: #error In <blitz/config.h>: A working template implementation is required by Blitz++ (you may need to rerun the compiler/bzconfig script)
这需要更新。
./blitz/gnu/bzconfig.h:4:/* blitz/gnu/bzconfig.h. Generated automatically at end of configure. */
./configure.ac:159:# autoconf replacement of bzconfig
你知道了,这些bzconfig.h
文件应该是由configure
生成的。
./ChangeLog.1:1787: will now replace the old file that was generate with the bzconfig
这可能是切换到 autoconf 的变化。
./INSTALL:107: 2. Go into the compiler subdirectory and run the bzconfig
这需要更新。这就是让你寻找bzconfig
的原因吗?
./README:27:compiler Compiler tests (used with obsolete bzconfig script)
需要更新,不再包含compiler
目录。
【讨论】:
【参考方案2】:据我所知,您是通过测量单个矩阵乘以标量的速度来判断每个矩阵库的性能。由于其基于模板的策略,Armadillo 将在这方面做得很好,将每个乘法分解为大多数编译器的可并行代码。
但我建议您需要重新考虑您的测试范围和方法。例如,您忽略了每个 BLAS 实现。您需要的 BLAS 函数是 dscal。供应商为您的特定 CPU 提供的实现可能会做得很好。
更相关的是,任何合理的向量库都需要能够做更多的事情:矩阵乘法、点积、向量长度、转置等等,您的测试没有解决这些问题。您的测试正好解决了两件事:元素分配,实际上这绝不是向量库的瓶颈,以及标量/向量乘法,这是每个 CPU 制造商提供的 BLAS 1 级函数。
讨论了 BLAS 1 级与编译器发出的代码here。
tl:博士; use Armadillo with BLAS and LAPACK native libraries linked in for your platform.
【讨论】:
使用诸如“大人讨论...”之类的语言在外观上是相当居高临下和不专业的。【参考方案3】:我的测试表明 boost 数组与原生/硬编码 C++ 代码具有相同的性能。
您需要使用激活的编译器优化来比较它们。那是:
-O3
-DNDEBUG
-DBOOST_UBLAS_NDEBUG
-DBOOST_DISABLE_ASSERTS
-DARMA_NO_DEBUG
...
当我测试 (em++) 时,当您停用它的断言、使用 -O3
启用 3 级优化等时,Boost 的执行速度至少快了 10 倍。任何公平的比较都应该使用这些标志。
【讨论】:
以上是关于比较 blitz++、犰狳、boost::MultiArray的主要内容,如果未能解决你的问题,请参考以下文章