我编写了一个函数,将10x10维的本征矩阵相乘。然后,我编写了一个朴素的乘法函数CustomMultiply,它比Eigen的实现快2倍。
我尝试了几个不同的编译标志,例如-O2和-O3,它们没有什么不同。
#include <Eigen/Core>
constexpr int dimension = 10;
using Matrix = Eigen::Matrix<double, dimension, dimension>;
Matrix CustomMultiply(const Matrix& a, const Matrix& b) {
Matrix result = Matrix::Zero();
for (int bcol_idx = 0; bcol_idx < dimension; ++bcol_idx) {
for (int brow_idx = 0; brow_idx < dimension; ++brow_idx) {
result.col(bcol_idx).noalias() += a.col(brow_idx) * b(brow_idx, bcol_idx);
}
}
return result;
}
Matrix PairwiseMultiplyEachMatrixNoAlias(int num_repetitions, const std::vector<Matrix>& input) {
Matrix acc = Matrix::Zero();
for (int i = 0; i < num_repetitions; ++i) {
for (const auto& matrix_a : input) {
for (const auto& matrix_b : input) {
acc.noalias() += matrix_a * matrix_b;
}
}
}
return acc;
}
Matrix PairwiseMultiplyEachMatrixCustom(int num_repetitions, const std::vector<Matrix>& input) {
Matrix acc = Matrix::Zero();
for (int i = 0; i < num_repetitions; ++i) {
for (const auto& matrix_a : input) {
for (const auto& matrix_b : input) {
acc.noalias() += CustomMultiply(matrix_a, matrix_b);
}
}
}
return acc;
}
Run Code Online (Sandbox Code Playgroud)
PairwiseMultiplyEachMatrixNoAliasPairwiseMultiplyEachMatrixCustom当我传入100个随机矩阵input并使用100作为时,我的计算机上的速度要慢2倍num_repetitions。我的机器详细信息:Intel Xeon CPU E5-2630 v4,Ubuntu 16.04,Eigen 3
更新:在注释中进行了有益的讨论之后,进行了以下修改后,结果保持不变
num_repetitions = 1 和 input.size() = 1000 .lazyProduct()和使用.eval()实际上导致进一步减速 -march=native -DNDEBUG更新2:
根据@dtell在Google Benchmark库中的发现,我发现了一个有趣的结果。将2个矩阵与Eigen相乘的速度比自定义快,但是将许多矩阵与Eigen相乘的速度慢2倍,这与以前的发现一致。
这是我的Google Benchmark代码。(注意:GenerateRandomMatrices()下面的函数中有一个旁白,现在已修复。)
#include <Eigen/Core>
#include <Eigen/StdVector>
#include <benchmark/benchmark.h>
constexpr int dimension = 10;
constexpr int num_random_matrices = 10;
using Matrix = Eigen::Matrix<double, dimension, dimension>;
using Eigen_std_vector = std::vector<Matrix,Eigen::aligned_allocator<Matrix>>;
Eigen_std_vector GetRandomMatrices(int num_matrices) {
Eigen_std_vector matrices;
for (int i = 0; i < num_matrices; ++i) {
matrices.push_back(Matrix::Random());
}
return matrices;
}
Matrix CustomMultiply(const Matrix& a, const Matrix& b) {
Matrix result = Matrix::Zero();
for (int bcol_idx = 0; bcol_idx < dimension; ++bcol_idx) {
for (int brow_idx = 0; brow_idx < dimension; ++brow_idx) {
result.col(bcol_idx).noalias() += a.col(brow_idx) * b(brow_idx, bcol_idx);
}
}
return result;
}
Matrix PairwiseMultiplyEachMatrixNoAlias(int num_repetitions, const Eigen_std_vector& input) {
Matrix acc = Matrix::Zero();
for (int i = 0; i < num_repetitions; ++i) {
for (const auto& matrix_a : input) {
for (const auto& matrix_b : input) {
acc.noalias() += matrix_a * matrix_b;
}
}
}
return acc;
}
Matrix PairwiseMultiplyEachMatrixCustom(int num_repetitions, const Eigen_std_vector& input) {
Matrix acc = Matrix::Zero();
for (int i = 0; i < num_repetitions; ++i) {
for (const auto& matrix_a : input) {
for (const auto& matrix_b : input) {
acc.noalias() += CustomMultiply(matrix_a, matrix_b);
}
}
}
return acc;
}
void BM_PairwiseMultiplyEachMatrixNoAlias(benchmark::State& state) {
// Perform setup here
const auto random_matrices = GetRandomMatrices(num_random_matrices);
for (auto _ : state) {
benchmark::DoNotOptimize(PairwiseMultiplyEachMatrixNoAlias(1, random_matrices));
}
}
BENCHMARK(BM_PairwiseMultiplyEachMatrixNoAlias);
void BM_PairwiseMultiplyEachMatrixCustom(benchmark::State& state) {
// Perform setup here
const auto random_matrices = GetRandomMatrices(num_random_matrices);
for (auto _ : state) {
benchmark::DoNotOptimize(PairwiseMultiplyEachMatrixCustom(1, random_matrices));
}
}
BENCHMARK(BM_PairwiseMultiplyEachMatrixCustom);
void BM_MultiplySingle(benchmark::State& state) {
// Perform setup here
const auto random_matrices = GetRandomMatrices(2);
for (auto _ : state) {
benchmark::DoNotOptimize((random_matrices[0] * random_matrices[1]).eval());
}
}
BENCHMARK(BM_MultiplySingle);
void BM_MultiplySingleCustom(benchmark::State& state) {
// Perform setup here
const auto random_matrices = GetRandomMatrices(2);
for (auto _ : state) {
benchmark::DoNotOptimize(CustomMultiply(random_matrices[0], random_matrices[1]));
}
}
BENCHMARK(BM_MultiplySingleCustom);
double TestCustom() {
const Matrix a = Matrix::Random();
const Matrix b = Matrix::Random();
const Matrix c = a * b;
const Matrix custom_c = CustomMultiply(a, b);
const double err = (c - custom_c).squaredNorm();
return err;
}
// Just sanity check the multiplication
void BM_TestCustom(benchmark::State& state) {
if (TestCustom() > 1e-10) {
exit(-1);
}
}
BENCHMARK(BM_TestCustom);
Run Code Online (Sandbox Code Playgroud)
这产生了以下神秘的报告
Run on (20 X 3100 MHz CPU s)
CPU Caches:
L1 Data 32K (x10)
L1 Instruction 32K (x10)
L2 Unified 256K (x10)
L3 Unified 25600K (x1)
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
----------------------------------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------------------------------
BM_PairwiseMultiplyEachMatrixNoAlias 28283 ns 28285 ns 20250
BM_PairwiseMultiplyEachMatrixCustom 14442 ns 14443 ns 48488
BM_MultiplySingle 791 ns 791 ns 876969
BM_MultiplySingleCustom 874 ns 874 ns 802052
BM_TestCustom 0 ns 0 ns 0
Run Code Online (Sandbox Code Playgroud)
我目前的假设是,速度下降归因于指令高速缓存未命中。Eigen的矩阵乘法功能可能会对指令缓存造成不良影响。
也许对VTune有更多经验的人可以告诉我我是否正确解释了这个结果。DSB是已解码的指令高速缓存,而MITE与指令解码器的带宽有关。Eigen版本显示大多数指令都缺少DSB(66%的未命中率),并且由于MITE带宽而导致停顿的明显增加。
更新3:在收到有关单个版本的custom更快的报告后,我还在计算机上复制了它。这与@dtell在其计算机上的原始发现背道而驰。
CPU Caches:
L1 Data 32K (x10)
L1 Instruction 32K (x10)
L2 Unified 256K (x10)
L3 Unified 25600K (x1)
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
----------------------------------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------------------------------
BM_PairwiseMultiplyEachMatrixNoAlias 34787 ns 34789 ns 16477
BM_PairwiseMultiplyEachMatrixCustom 17901 ns 17902 ns 37759
BM_MultiplySingle 349 ns 349 ns 2054295
BM_MultiplySingleCustom 178 ns 178 ns 4624183
BM_TestCustom 0 ns 0 ns 0
Run Code Online (Sandbox Code Playgroud)
我想知道在我以前的基准测试结果中是否没有优化标记。无论如何,我认为问题已得到证实,当小矩阵相乘时,本征会产生开销。如果那里的任何人都拥有不使用uop缓存的计算机,那么我希望了解这种减速是否较不严重。
我已经使用适当的基准库(即Google Benchmark)重写了您的代码,并且无法重现您的测量结果。
我的-O0第二个模板参数是矩阵维度的结果:
Running ./benchmark
Run on (12 X 2900 MHz CPU s)
CPU Caches:
L1 Data 32K (x6)
L1 Instruction 32K (x6)
L2 Unified 262K (x6)
L3 Unified 12582K (x1)
---------------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------------
BM_CustomMultiply<double, 3> 5391 ns 5389 ns 105066
BM_CustomMultiply<double, 4> 9365 ns 9364 ns 73649
BM_CustomMultiply<double, 5> 15349 ns 15349 ns 44008
BM_CustomMultiply<double, 6> 20953 ns 20947 ns 32230
BM_CustomMultiply<double, 7> 33328 ns 33318 ns 21584
BM_CustomMultiply<double, 8> 44237 ns 44230 ns 15500
BM_CustomMultiply<double, 9> 57142 ns 57140 ns 11953
BM_CustomMultiply<double, 10> 69382 ns 69382 ns 9998
BM_EigenMultiply<double, 3> 2335 ns 2335 ns 295458
BM_EigenMultiply<double, 4> 1613 ns 1613 ns 457382
BM_EigenMultiply<double, 5> 4791 ns 4791 ns 142992
BM_EigenMultiply<double, 6> 3471 ns 3469 ns 206002
BM_EigenMultiply<double, 7> 9052 ns 9051 ns 78135
BM_EigenMultiply<double, 8> 8655 ns 8655 ns 81717
BM_EigenMultiply<double, 9> 11446 ns 11399 ns 67001
BM_EigenMultiply<double, 10> 15092 ns 15053 ns 46924
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,Google 基准测试使用的迭代次数比您的基准测试高出几个数量级。微基准测试非常困难,尤其是当您处理几百纳秒的执行时间时。
公平地说,调用自定义函数涉及一个副本并手动内联它,需要几纳秒的时间,但仍然没有击败 Eigen。
手动内联测量CustomMultiply和-O2 -DNDEBUG -march=native:
Running ./benchmark
Run on (12 X 2900 MHz CPU s)
CPU Caches:
L1 Data 32K (x6)
L1 Instruction 32K (x6)
L2 Unified 262K (x6)
L3 Unified 12582K (x1)
---------------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------------
BM_CustomMultiply<double, 3> 51 ns 51 ns 11108114
BM_CustomMultiply<double, 4> 88 ns 88 ns 7683611
BM_CustomMultiply<double, 5> 147 ns 147 ns 4642341
BM_CustomMultiply<double, 6> 213 ns 213 ns 3205627
BM_CustomMultiply<double, 7> 308 ns 308 ns 2246391
BM_CustomMultiply<double, 8> 365 ns 365 ns 1904860
BM_CustomMultiply<double, 9> 556 ns 556 ns 1254953
BM_CustomMultiply<double, 10> 661 ns 661 ns 1027825
BM_EigenMultiply<double, 3> 39 ns 39 ns 17918807
BM_EigenMultiply<double, 4> 69 ns 69 ns 9931755
BM_EigenMultiply<double, 5> 119 ns 119 ns 5801185
BM_EigenMultiply<double, 6> 178 ns 178 ns 3838772
BM_EigenMultiply<double, 7> 256 ns 256 ns 2692898
BM_EigenMultiply<double, 8> 385 ns 385 ns 1826598
BM_EigenMultiply<double, 9> 546 ns 546 ns 1271687
BM_EigenMultiply<double, 10> 644 ns 644 ns 1104798
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
356 次 |
| 最近记录: |