Eigen3: Как получить доступ к матричным коэффициентам в операциях, критичных к производительности? - PullRequest
0 голосов
/ 16 ноября 2018

Я пытаюсь оптимизировать критические операции в C ++, основанные на Eigen3.Для меня не ясно, какой тип операций доступа с коэффициентами приведет к снижению производительности во время выполнения или когда компилятор сделает хорошую работу.Чтобы попытаться определить источник моей путаницы, я публикую нижеприведенный пример, реализованный несколькими различными способами, вместе с некоторыми гипотезами для каждого.

Вот еще несколько подробностей:

  • Матрица M будет оставаться постоянной в течение большей части программы
  • критическая_функция действительно вызывается много раз, и поэтому она встроена

Может ли кто-нибудь уточнить, какой подход будет лучшим с точки зрения производительности?Я могу быть смущен влиянием стоимости ссылок, разыменования и т. Д.

Вариант 1: Прямой доступ к матричным коэффициентам

#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;
};

Гипотеза :

  • M (1,1) приводит к постоянному разыменованию, что влечет за собой затраты, поскольку к вычислению смещения будут добавляться циклы (это не массив, но неясно, как компилятор управляет этим)

Вариант 2: Создание копии коэффициента, который нас интересует

#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;
};

Гипотеза :

  • Доступ x генерирует меньше циклов, чем при обращении к M (1, 1) , поэтому предпочтительнее варианта 1.
  • x действительно содержит то же значение, что и M(1,1) , но несет в себе важный риск того, что эти данные будут дублироваться, поэтому этого следует избегать при ведении кода.

Вариант 3: Использование ссылок

#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;
};

Гипотеза :

  • Имея одну ссылку x будет родовт. е. меньше циклов, чем постоянно, ссылаясь на M (1,1) внутри области действия функции.
  • Эта потенциальная оптимизация оказывает влияние только внутри критической функции, но не будет перенесено во внешнюю область, такую ​​как цикл, вызывающий функцию много раз.

Редактировать

Типы были исправлены, чтобы удвоиться (из int илиfloat), чтобы соответствовать Matrix3d.

1 Ответ

0 голосов
/ 28 ноября 2018

Короче, не беспокойтесь и не пишите M(1,1).

Если вы имеете дело с матрицами времени компиляции, такими как Matrix3d, и индексами, известными во время компиляции, то вычисления индексации, включенные в M(1,1), будут полностью оптимизированы любым компилятором. Другими словами, два следующих фрагмента сгенерируют одну и ту же сборку:

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; }
};

Таким образом, вариант 2 будет хуже, а вариант 3 также может снизить производительность, поскольку вы вводите указатель.

...