【问题标题】:How to speed up Eigen library's matrix product?如何加速特征库矩阵乘积?
【发布时间】:2013-01-24 20:20:25
【问题描述】:

我正在使用 Eigen 库研究两个大矩阵的简单乘法。对于相同大小的矩阵,这种乘法似乎明显慢于 Matlab 和 Python。

有什么办法可以让 Eigen 运算更快?

问题详情

X : 随机 1000 x 50000 矩阵

Y : 随机 50000 x 300 矩阵

计时实验(在我 2011 年末的 Macbook Pro 上)

使用 Matlab:X*Y 需要 ~1.3 秒

使用 Enthought Python:numpy.dot(X, Y) 大约需要 2.2 秒

使用 Eigen:X*Y 需要 ~2.7 秒

特征细节

您可以获得我的特征码(作为 MEX 函数):https://gist.github.com/michaelchughes/4742878

这个 MEX 函数从 Matlab 中读取两个矩阵,并返回它们的乘积。

在没有矩阵乘积运算的情况下运行这个 MEX 函数(即只进行 IO)产生的开销可以忽略不计,因此函数和 Matlab 之间的 IO 并不能解释性能上的巨大差异。这显然是实际的矩阵乘积运算。

我正在使用 g++ 编译,带有这些优化标志:“-O3 -DNDEBUG”

我正在使用最新的稳定 Eigen 头文件 (3.1.2)。

关于如何提高 Eigen 性能的任何建议?谁能复制我看到的差距?

更新 编译器似乎真的很重要。最初的 Eigen 计时是使用 Apple XCode 的 g++ 版本完成的:llvm-g++-4.2。

当我使用通过 MacPorts 下载的 g++-4.7(相同的 CXXOPTIMFLAGS)时,我得到 2.4 秒而不是 2.7 秒。

任何其他关于如何更好地编译的建议将不胜感激。

您还可以获取此实验的原始 C++ 代码:https://gist.github.com/michaelchughes/4747789

./MatProdEigen 1000 50000 300

在g++-4.7下报2.4秒

【问题讨论】:

  • 你知道它实现了什么算法吗?看起来它可能只是使用了一个糟糕的矩阵乘法算法。要尝试的另一件事是启用自动矢量化:gcc.gnu.org/projects/tree-ssa/vectorization.html(默认情况下不启用,我不认为......好吧,也许。不确定)。如果您在英特尔机器上,请尝试使用英特尔编译器......我注意到它在优化方面总是优于其他所有人。也可以在这里查看eigen.tuxfamily.org/index.php?title=FAQ#Vectorization
  • @thang:Eigen 是为线性代数设计的,所以如果使用的算法那么糟糕,我会感到惊讶。根据您的链接,我使用“-O3”优化标志默认启用树矢量化,所以这不是问题 AFAIK。如果没有其他建议出现,我可能会尝试英特尔编译器。
  • @MikeHuges,您还可以尝试绘制随着矩阵大小增加的增长率,并可能对正在发生的事情提供一些提示。这应该表明它使用哪种算法。或者,深入研究他们的源代码或文档。
  • 嗨,我用了大约 260 秒在我的机器上运行测试 C++ 代码,我使用 VS2012 和 windows,我的处理器是核心 i5-4570。在测试矩阵乘法时,我也花了大约 1.3 秒。这很有线。

标签: matlab mex eigen


【解决方案1】:

Eigen 没有利用英特尔在 Sandy Bridge 架构中引入的 AVX 指令。这可能解释了 Eigen 和 MATLAB 之间的大部分性能差异。 我在https://bitbucket.org/benoitsteiner/eigen 找到了一个添加对 AVX 支持的分支,但据我所知,它还没有被合并到 Eigen 主干中。

【讨论】:

  • 是的,FMA 又增加了两倍。我不知道是否有任何库支持 FMA。
【解决方案2】:

首先,在进行性能比较时,请确保您禁用了涡轮增压 (TB)。在我的系统上,使用 macport 的 gcc 4.5 并且没有涡轮增压,我得到 3.5 秒,相当于 8.4 GFLOPS,而我的 2.3 核心 i7 的理论峰值是 9.2GFLOPS,所以还不错。

MatLab 基于 Intel MKL,从报告的性能来看,它显然使用了多线程版本。像 Eigen 这样的小型库不太可能在自己的 CPU 上击败 Intel!

Numpy 可以使用任何 BLAS 库、Atlas、MKL、OpenBLAS、eigen-blas 等。我猜在你的情况下它使用的 Atlas 也很快。

最后,您可以通过以下方式获得更好的性能:通过使用 -fopenmp 编译在 Eigen 中启用多线程。默认情况下,Eigen 使用 OpenMP 定义的默认线程数作为线程数。不幸的是,这个数字对应于逻辑内核的数量,而不是物理内核的数量,因此请确保禁用超线程或将 OMP_NUM_THREADS 环境变量定义为内核的物理数量。在这里我得到 1.25s(没有 TB)和 0.95s 有 TB。

【讨论】:

  • 关于多线程的良好调用:Matlab 多线程似乎可以解释大部分差异。当我使用“-singleCompThread”命令行选项时,Matlab 的时钟频率约为 2.4 秒,与 Eigen 相同。
  • 在这种情况下,除了逻辑核心的数量之外,我想不出有什么好的理由可以将 OMP_NUM_THREADS 设置为其他任何值。如果您将其设置为启用了 HT 的物理内核数,您只会空闲 50% 的 CPU...
  • 基本上,HT 允许通过在同一个物理内核上运行两个线程来隐藏内存延迟并增强指令流水线。然而,在 Eigen 中实现良好的矩阵乘积例程已经占据了近 100% 的算术单元,这意味着流水线已经很完美,并且内存延迟已经很好地隐藏了。在这种情况下,HT只能破坏性能。例如,两个线程将同时访问同一个 L1 缓存资源,从而抵消了 L1 缓存的好处。
  • 最后我检查了 Eigen 只使用了 SSE,而 MKL 使用了 AVX。这是两倍的损失。从他们开始,Haswell 就推出了 FMA。我不知道 MKL 是否支持 FMA,但如果支持,那么这可能是另一个导致两个损失的因素(总共四个)。我自己编写的 GEMM 代码是使用 OpenMP 和 FMA 的 Eigen 代码的两倍多。
【解决方案3】:

Matlab 速度更快的原因是它使用了英特尔 MKL。 Eigen 也可以使用(见here),但你当然需要购买。

话虽如此,Eigen 变慢的原因有很多。要比较 python、matlab 和 Eigen,您确实需要用各自的语言编写三个等效版本的操作。另请注意,Matlab 会缓存结果,因此您确实需要从新的 Matlab 会话开始,以确保它的魔力不会愚弄您。

另外,Matlab's Mex overhead is not nonexistent。那里的 OP 报告新版本“修复”了问题,但如果所有开销都已完全清除,我会感到惊讶。

【讨论】:

  • 对于我的特殊情况(使用 R2011b),MEX 调用的开销不是主要原因。为了验证,我写了这个测试的pure C++ version,它给出的时间与我给 MEX 计时的时间相同(~2.4 秒)。我还运行了我的MEX version,因此它只通过注释掉执行矩阵乘积的行来执行 IO。此 IO 仅位(所有开销)在 ~.001 秒内运行。
  • 此外,Matlab“缓存结果”位似乎也无法解释事情。我运行了许多新的开始(使用命令行界面)并且所有时钟都在 ~1.3 秒。这显然是多线程(见下面@ggael 的帖子)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-08
相关资源
最近更新 更多