特征3.3.0对3.2.10的性能回归
performance regression with Eigen 3.3.0 vs. 3.2.10?
我们只是在将代码库移植到eigen 3.3的过程中(与所有32个字节对准问题相当一项工作)。但是,与期望相反,在某些地方,表现似乎受到了严重影响(我期待对FMA和AVX的额外支持...)。其中包括特征值分解和矩阵*矩阵。transpose()*矢量产物。我写了两个最小的工作示例以演示。
使用Intel Core i7-4930k CPU(3.40GHz),所有测试在最新的Arch Linux系统上运行,并使用G 版本6.2.1进行编译。
1。特征值分解:
直接的自我偶会特征值分解需要特征3.3.0的两倍,就像3.2.10一样。
。文件test_eigen_EVD.cpp
:
#define EIGEN_DONT_PARALLELIZE
#include <Eigen/Dense>
#include <Eigen/Eigenvalues>
#define SIZE 200
using namespace Eigen;
int main (int argc, char* argv[])
{
MatrixXf mat = MatrixXf::Random(SIZE,SIZE);
SelfAdjointEigenSolver<MatrixXf> eig;
for (int n = 0; n < 1000; ++n)
eig.compute (mat);
return 0;
}
测试结果:
eigen-3.2.10:
g++ -march=native -O2 -DNDEBUG -isystem eigen-3.2.10 test_eigen_EVD.cpp -o test_eigen_EVD && time ./test_eigen_EVD real 0m5.136s user 0m5.133s sys 0m0.000s
eigen-3.3.0:
g++ -march=native -O2 -DNDEBUG -isystem eigen-3.3.0 test_eigen_EVD.cpp -o test_eigen_EVD && time ./test_eigen_EVD real 0m11.008s user 0m11.007s sys 0m0.000s
不确定会导致这一点的原因,但是如果有人可以看到一种维持特征3.3的性能的方法,我想知道它!
2。矩阵*matrix.transpose()*矢量产品:
这个特定的示例需要使用特征3.3.0 ...
更长的200倍。文件test_eigen_products.cpp
:
#define EIGEN_DONT_PARALLELIZE
#include <Eigen/Dense>
#define SIZE 200
using namespace Eigen;
int main (int argc, char* argv[])
{
MatrixXf mat = MatrixXf::Random(SIZE,SIZE);
VectorXf vec = VectorXf::Random(SIZE);
for (int n = 0; n < 50; ++n)
vec = mat * mat.transpose() * VectorXf::Random(SIZE);
return vec[0] == 0.0;
}
测试结果:
eigen-3.2.10:
g++ -march=native -O2 -DNDEBUG -isystem eigen-3.2.10 test_eigen_products.cpp -o test_eigen_products && time ./test_eigen_products real 0m0.040s user 0m0.037s sys 0m0.000s
eigen-3.3.0:
g++ -march=native -O2 -DNDEBUG -isystem eigen-3.3.0 test_eigen_products.cpp -o test_eigen_products && time ./test_eigen_products real 0m8.112s user 0m7.700s sys 0m0.410s
这样的循环中的行中添加括号:
vec = mat * ( mat.transpose() * VectorXf::Random(SIZE) );
具有巨大的差异,两个特征版本的性能都同样好(实际上3.3.0稍好),并且比未支付的3.2.10情况更快。所以有一个修复程序。尽管如此,奇怪的是3.3.0会为此挣扎。
我不知道这是否是一个错误,但我想如果需要解决的话,这是值得举报的。也许我只是做错了...
任何想法都赞赏。干杯,唐纳德。
编辑
正如Ggael指出的那样,如果使用clang++
进行编译,则特征3.3中的EVD速度更快,或使用-O3
使用g++
。这就是问题1修复。
问题2并不是真正的问题,因为我只能将括号施加到最有效的操作顺序。但是只是为了完整:评估这些操作的某个地方似乎确实存在缺陷。EIGEN是一件令人难以置信的软件,我认为这可能应该是固定的。这是MWE的修改版本,只是表明它不太可能与第一个临时产品相关(至少据我所知):
#define EIGEN_DONT_PARALLELIZE
#include <Eigen/Dense>
#include <iostream>
#define SIZE 200
using namespace Eigen;
int main (int argc, char* argv[])
{
VectorXf vec (SIZE), vecsum (SIZE);
MatrixXf mat (SIZE,SIZE);
for (int n = 0; n < 50; ++n) {
mat = MatrixXf::Random(SIZE,SIZE);
vec = VectorXf::Random(SIZE);
vecsum += mat * mat.transpose() * VectorXf::Random(SIZE);
}
std::cout << vecsum.norm() << std::endl;
return 0;
}
在此示例中,操作数全部都在循环中初始化,并且在vecsum
中累积的结果,因此编译器无法预先计算任何内容或优化不必要的计算。这显示了完全相同的行为(这次使用clang++ -O3
测试(版本3.9.0):
$ clang++ -march=native -O3 -DNDEBUG -isystem eigen-3.2.10 test_eigen_products.cpp -o test_eigen_products && time ./test_eigen_products
5467.82
real 0m0.060s
user 0m0.057s
sys 0m0.000s
$ clang++ -march=native -O3 -DNDEBUG -isystem eigen-3.3.0 test_eigen_products.cpp -o test_eigen_products && time ./test_eigen_products
5467.82
real 0m4.225s
user 0m3.873s
sys 0m0.350s
相同的结果,但执行时间大不相同。值得庆幸的是,通过将括号放在正确的位置可以很容易地解决,但是在特征3.3的操作评估中,似乎确实有回归。随着mat.transpose() * VectorXf::Random(SIZE)
部分周围的括号,两个特征版本的执行时间均缩短为0.020左右(因此在这种情况下,特征3.2.10也显然受益)。至少这意味着我们可以继续从特征中获得出色的表现!
与此同时,我将接受Ggael的答案,这是我继续前进所需的一切。
对于evd,我无法用clang复制。使用GCC,您需要-O3
来避免嵌入问题。然后,使用两个编译器,特征3.3将提供33%的加速。
编辑我先前关于matrix*matrix*vector
产品的答案是错误的。这是特征3.3.0中的缺点,将在特征3.3.1中固定。对于记录,我在这里留下我以前的分析,该分析仍然部分有效:
您注意到,您应该真正添加括号以执行两个
matrix*vector
产品而不是大型matrix*matrix
产品。 然后,速度差很容易通过以下事实来解释。 立即评估嵌套的matrix*matrix
产品(在 嵌套时间),而在3.3中,在评估时进行评估, 在operator=
中。这意味着在3.2中,循环等同于:for (int n = 0; n < 50; ++n) { MatrixXf tmp = mat * mat.transpose(); vec = tmp * VectorXf::Random(SIZE); }
因此,编译器可以将
tmp
移出循环。生产代码 不应依靠编译器来执行此类任务,而是 明确移动恒定表达式外部循环。
这是事实,除非在实践中编译器不够聪明,无法将临时性移出循环。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- OpenMP阵列性能较差
- 递归列出所有目录中的C++与Python与Ruby的性能
- 大小相等但成员数量不同的结构之间的性能差异
- 为什么constexpr的性能比正常表达式差
- 在类中使用随机生成器时出现性能问题
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 海湾合作委员会 ARM 性能下降
- GCC 和 Clang 代码性能的巨大差异
- 在容量内调整矢量大小时的性能影响
- 了解算法的性能差异(如果以不同的编程语言实现)
- 未达到的情况会影响开关外壳性能
- QStringList vs list<shared_ptr<QString>> 性能比较C++
- 是否总是可以将使用递归编写的程序重写为不使用递归的程序C++,性能观点是什么?
- 哪种方法更好,性能明智
- C++ 特征库:引用的性能开销<>
- 与多个 for 循环与单个 for 循环 wrt 相关的性能从多映射获取数据
- 基于范围的 for 循环range_declaration中各种说明符之间的性能差异
- 特征3.3.0对3.2.10的性能回归
- std::启用 C++11 时的矢量性能回归