Собственная библиотека C ++: снижение производительности Ref <> - PullRequest
0 голосов
/ 14 октября 2018

Я пишу библиотеку общего назначения, использующую Eigen для вычислительной механики, которая в основном работает с матрицами размером 6x6 и векторами размером 6x1.Я рекомендую использовать шаблон Eigen::Ref<>, чтобы его можно было использовать также для сегментов и блоков, как описано в http://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html и Правильное использование класса Eigen :: Ref <>

Однако небольшое сравнение производительности показывает, что Eigen::Ref имеет значительные издержки для таких небольших функций по сравнению со стандартными ссылками на c ++:

#include <ctime>
#include <iostream>
#include "Eigen/Core"


Eigen::Matrix<double, 6, 6> testRef(const Eigen::Ref<const Eigen::Matrix<double, 6, 6>>& A)
{
    Eigen::Matrix<double, 6, 6> temp = (A * A) * A;
    temp.diagonal().setOnes();
    return temp;
}

Eigen::Matrix<double, 6, 6> testNoRef(const Eigen::Matrix<double, 6, 6>& A)
{
    Eigen::Matrix<double, 6, 6> temp = (A * A) * A; 
    temp.diagonal().setOnes();
    return temp;
}


int main(){

  using namespace std;

  int cycles = 10000000;
  Eigen::Matrix<double, 6, 6> testMat;
  testMat = Eigen::Matrix<double, 6, 6>::Ones();

  clock_t begin = clock();

  for(int i = 0; i < cycles; i++)
      testMat = testRef(testMat);

  clock_t end = clock();


  double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;

  std::cout << "Ref: " << elapsed_secs << std::endl;

  begin = clock();

  for(int i = 0; i < cycles; i++)
      testMat = testNoRef(testMat);
  end = clock();

  elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;

  std::cout << "noRef : " << elapsed_secs << std::endl;


    return 0;
}

Вывод с gcc -O3:

Ref: 1.64066
noRef : 1.1281

Таким образом, кажется, что Eigen::Ref имеет значительные накладные расходы, по крайней мере, в случаях с низким фактическим вычислительным усилием.С другой стороны, подход с использованием const Eigen::Matrix<double, 6, 6>& A приводит к ненужным копиям, если переданы блоки или сегменты:

#include <Eigen/Core>
#include <iostream>


void test( const Eigen::Vector3d& a)
{
    std::cout << "addr in function " << &a << std::endl;
}

int main () {

    Eigen::Vector3d aa;
    aa << 1,2,3;
    std::cout << "addr outside function " << &aa << std::endl;

    test ( aa ) ;
    test ( aa.head(3) ) ;


    return 0;
}

Вывод:

addr outside function 0x7fff85d75960
addr in function 0x7fff85d75960
addr in function 0x7fff85d75980

Таким образом, этот подход исключен для общегоcase.

В качестве альтернативы можно создать шаблоны функций, используя Eigen::MatrixBase, как описано в документации.Однако это кажется неэффективным для больших библиотек, и его нельзя адаптировать к матрицам фиксированного размера (6x6, 6x1), как в моем случае.

Есть ли другая альтернатива?Какова общая рекомендация для больших библиотек общего назначения?

Заранее спасибо!

edit: Изменен первый пример эталонного теста в соответствии с рекомендациями в комментариях

1 Ответ

0 голосов
/ 14 октября 2018

С Ref<> вы платите цену потери двух данных (по сравнению с матрицей):

  1. Вы потеряли знание о том, что вход выровнен по памяти.
  2. Вы потерялизнание времени компиляции о том, что столбцы хранятся последовательно (и, следовательно, два столбца разделены 6 двойными).

Это классический компромисс между универсальностью и высочайшей производительностью.

...