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
所以这似乎是值得的。
我确实使用 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
所以这似乎是值得的。