简体   繁体   中英

Eigen3: How to access matrix coefficients in performance critical operations?

I am trying to optimize critical operations in C++ which rely on Eigen3. It is not clear for me what type of coefficient access operations would lead to runtime performance costs, or when would the compiler will make a good job. To try to pinpoint the source of my confusion I'm posting an example below implemented in a few different ways, together with some hypothesis for each.

Here a few more details:

  • The matrix M will remain constant throughout most of the program
  • critical_function is indeed called many times and this is why it is inlined

Could someone clarify which approach would be the best one in terms of performance? I might be confused on the impact cost of references, dereferencing, etc.

Option 1: Directly accessing matrix coefficients

#include <Eigen/Dense>
class A{
    A(){
        // Assume M has the right numbers
    }

    // This function will be called many many times, inside loops
    inline void critical_function()
    {
        // Do many operations using M(1, 1), for example:
        double y = 1 / M(1, 1);
        // ... some more code using M(1, 1)
    }
private:
    Eigen::Matrix3d M;
};

Hypothesis :

  • M(1,1) leads to constant dereferencing, incurring costs, as cycles will be added to computing an offset (this is not an array, but it is not clear how the compiler is managing this)

Option 2: Creating a copy of the coefficient we care about

#include <Eigen/Dense>
class A{
    A(){
        // Assume M has the right numbers
        x = M(1, 1);
    }

    // This function will be called many many times, inside loops
    inline void critical_function()
    {
        // Do many operations using x, for example:
        double y = 1 / x;
        // ... some more code using x
    }
private:
    double x;
    Eigen::Matrix3d M;
};

Hypothesis :

  • Accessing x generates less cycles than accessing M(1, 1) , thus it is preferable to Option 1.
  • x indeed contains the same value as M(1,1) but carries the important risk of ensuring this data is duplicated, so this needs to be avoided for code maintenance.

Option 3: Making use of references

#include <Eigen/Dense>
class A{
    A(){
        // Assume M has the right numbers
    }

    // This function will be called many many times, inside loops
    inline void critical_function()
    {
        auto & x = M(1, 1);
        // Do many operations using x, for example:
        double y = 1 / x;
        // ... some more code using x
    }
private:
    Eigen::Matrix3d M;
};

Hypothesis :

  • Having a single reference x will generate less cycles than constantly referring to M(1,1) inside the scope of the function.
  • This potential optimization has an impact only inside critical_function , but will not carry over in an external scope, such as a loop calling the function many times.

Edit

The types were corrected to double (from int or float), to be consistent with Matrix3d.

In short, don't bother and write M(1,1) .

If you're dealing with compile-time matrices like Matrix3d and indices known at compile-time, then the indexing computations involved in M(1,1) will be completely optimized away by any compiler. In other words, the two following snippets will generate the same assembly:

struct A {
  Matrix3d M;
  void foo() { double x = M(1,1); }
};

struct A {
  double a, b, c, d, e, f, g, h, i;
  void foo() { double x = e; }
};

So option 2 will be worse, and option 3 might also reduce performance because you introduce a pointer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM