本征矩阵乘法速度

Eigen Matrix Multiplication Speed

我正在尝试用 C++ 进行线性代数数值计算。我使用 Python Numpy 作为快速模型,我想找到一个 C++ 线性代数包来进一步加快速度。 Eigen 似乎是一个很好的起点。

我写了一个使用大密集矩阵乘法的小性能测试来测试处理速度。在 Numpy 中我是这样做的:

import numpy as np
import time

a = np.random.uniform(size = (5000, 5000))
b = np.random.uniform(size = (5000, 5000))
start = time.time()
c = np.dot(a, b)
print (time.time() - start) * 1000, 'ms'

在 C++ Eigen 中我是这样做的:

#include <time.h>
#include "Eigen/Dense"

using namespace std;
using namespace Eigen;

int main() {
    MatrixXf a = MatrixXf::Random(5000, 5000);
    MatrixXf b = MatrixXf::Random(5000, 5000);
    time_t start = clock();
    MatrixXf c = a * b;
    cout << (double)(clock() - start) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
    return 0;
}

我在文档和 Whosebug 上对编译优化标志进行了一些搜索。我尝试使用此命令编译程序:

g++ -g test.cpp -o test -Ofast -msse2

使用 -Ofast 优化标志编译的 C++ 可执行文件 运行 比简单的无优化编译快 30 倍或更多。它会在我的 2015 macbook pro 上 return 在大约 10000 毫秒内得到结果。

同时 Numpy 将 return 在大约 1800 毫秒内得到结果。

与 Numpy 相比,我期待使用 Eigen 的性能提升。然而,这却出乎我的意料。

是否有任何我遗漏的编译标志可以进一步提高 Eigen 的性能?或者是否有任何可以打开的多线程开关可以给我额外的性能提升?我只是好奇这个。

非常感谢!

2016 年 4 月 17 日编辑:

根据@ggael的回答做了一番搜索后,我得出了这个问题的答案。

对此的最佳解决方案是使用 link 编译为英特尔 MKL 作为 Eigen 的后端。对于 osx 系统,可以在 here. With MKL installed I tried to use the Intel MKL link line advisor 找到该库以启用对 Eigen 的 MKL 后端支持。

我以这种方式为所有 MKL 启用编译:

g++ -DEIGEN_USE_MKL_ALL -L${MKLROOT}/lib -lmkl_intel_lp64 -lmkl_core -lmkl_intel_thread -liomp5 -lpthread -lm -ldl -m64 -I${MKLROOT}/include -I. -Ofast -DNDEBUG test.cpp -o test

如果 MKLROOT 有任何环境变量错误,只需 运行 MKL 包中提供的环境设置脚本,默认安装在我设备上的 /opt/intel/mkl/bin。

在我的 2.5Ghz Macbook Pro 上,使用 MKL 作为特征后端,两个 5000x5000 运算的矩阵乘法将在大约 900 毫秒内完成。这比我设备上的 Python Numpy 快得多。

用VC2013编译你的小程序:

  • /fp:精确 - 10.5s
  • /fp:strict - 10.4s
  • /fp:快 - 10.3s
  • /fp:fast /arch:AVX2 - 6.6s
  • /fp:fast /arch:AVX2 /openmp - 2.7s

因此,使用 AVX/AVX2 并启用 OpenMP 将大有帮助。您也可以尝试链接 MKL (http://eigen.tuxfamily.org/dox/TopicUsingIntelMKL.html)。

回答OSX这边,首先回想一下OSX上g++其实是clang++的别名,目前Apple的clang版本不支持openmp。尽管如此,使用 Eigen3.3-beta-1 和默认的 clang++,我得到了 macbookpro 2.6Ghz:

$ clang++ -mfma -I ../eigen so_gemm_perf.cpp  -O3 -DNDEBUG  &&  ./a.out
2954.91ms

然后要获得对多线程的支持,您需要最新的 gcc 编译器,例如使用 homebrew 或 macport。这里使用来自 macport 的 gcc 5,我得到:

$ g++-mp-5 -mfma -I ../eigen so_gemm_perf.cpp  -O3 -DNDEBUG -fopenmp -Wa,-q && ./a.out
804.939ms

和 clang 3.9:

$ clang++-mp-3.9 -mfma -I ../eigen so_gemm_perf.cpp  -O3 -DNDEBUG -fopenmp  && ./a.out
806.16ms

请注意 osx 上的 gcc 不知道如何正确地执行 assemble AVX/FMA 指令,因此您需要告诉它使用本机 assembler 和 -Wa,-q旗帜。

最后,通过 devel 分支,您还可以告诉 Eigen 使用任何 BLAS 作为后端,例如来自 Apple 的 Accelerate,如下所示:

$ g++ -framework Accelerate -DEIGEN_USE_BLAS -O3 -DNDEBUG so_gemm_perf.cpp  -I ../eigen  && ./a.out
802.837ms