Eigen 中立方根的性能改进

performance improvements for cube root in Eigen

我确实使用 Valgrind 分析了我的代码(使用“带有调试信息的版本”构建),发现大量时间 (~25%) 花在了计算元素立方的一行上大矩阵的根。 现在,如果可能的话,我想加快这一步。

目前,我只是在使用 .pow( 1.0 / 3.0)。我想知道是否有办法改善这一点,也许可以使用 std::cbrt()?但是,如何将此函数传递给 Eigen 以计算元素立方根?

#include "iostream"
#include "eigen-3.3.7/Eigen/Dense"

using namespace Eigen;
int main() {
  // generate some random numbers
  VectorXd v = VectorXd::Random(10).array().abs() ;
  std::cout << v << std::endl << std::endl ;

  // calculate the cubic root
  VectorXd s = v.array().pow( 1.0 / 3.0 );
  std::cout << s << std::endl;
}

您可以使用 DenseBase::unaryExpr 和 C++ lambda:

VectorXd s = v.unaryExpr([](double coeff){ return std::cbrt(coeff); });

使用 Google Benchmark 的小型基准:

#include <Eigen/Dense>
#include <benchmark/benchmark.h>

using namespace Eigen;

static void BM_Pow(benchmark::State& state)
{
  VectorXd v = VectorXd::Random(state.range(0)).array().abs();
  VectorXd s;
  for (auto _ : state) {
    benchmark::DoNotOptimize(s = v.array().pow( 1.0 / 3.0 ));
    benchmark::ClobberMemory();
  }
}

static void BM_Cbrt(benchmark::State& state)
{
  VectorXd v = VectorXd::Random(state.range(0)).array().abs();
  VectorXd s;
  for (auto _ : state) {
    benchmark::DoNotOptimize(s = v.unaryExpr([](double coeff){ return std::cbrt(coeff); }));
    benchmark::ClobberMemory();
  }
}

BENCHMARK(BM_Pow) -> Range(4, 10000);
BENCHMARK(BM_Cbrt) -> Range(4, 10000);

BENCHMARK_MAIN();

使用 -O3 编译在我的机器上给出以下结果:

-----------------------------------------------------
Benchmark              Time           CPU Iterations
-----------------------------------------------------
BM_Pow/4              69 ns         69 ns   10099698
BM_Pow/8             134 ns        134 ns    5391874
BM_Pow/64           1043 ns       1043 ns     673401
BM_Pow/512          8476 ns       8474 ns      82371
BM_Pow/4096        68708 ns      68702 ns      10839
BM_Pow/10000      160833 ns     160566 ns       4222
BM_Cbrt/4             23 ns         23 ns   31538209
BM_Cbrt/8             45 ns         45 ns   15129345
BM_Cbrt/64           358 ns        358 ns    1968338
BM_Cbrt/512         2810 ns       2809 ns     254678
BM_Cbrt/4096       23926 ns      23855 ns      31430
BM_Cbrt/10000      55692 ns      55568 ns      12765

所以这似乎是值得的。