Хуже производительность с использованием Eigen, чем использование моего собственного класса

Несколько недель назад я задал вопрос о производительности матричного умножения.

Мне сказали, что для повышения производительности моей программы я должен использовать некоторые специализированные классы матриц, а не свой собственный класс.

Рекомендуемые пользователи StackOverflow:

  • uBLAS
  • Эйген
  • BLAS

Сначала мне захотелось использовать uBLAS, но, читая документацию, оказалось, что эта библиотека не поддерживает умножение матричной матрицы.

В конце концов я решил использовать библиотеку EIGEN. Поэтому я обменял свой класс матрицы на Eigen::MatrixXd - однако оказалось, что теперь мое приложение работает еще медленнее, чем раньше. Время до использования EIGEN составляло 68 секунд, и после замены моего матричного класса на EIGEN матричная программа запускалась в течение 87 секунд.

Части программы, которые занимают больше всего времени, выглядят так:

TemplateClusterBase* TemplateClusterBase::TransformTemplateOne( vector<Eigen::MatrixXd*>& pointVector, Eigen::MatrixXd& rotation ,Eigen::MatrixXd& scale,Eigen::MatrixXd& translation )
{   
    for (int i=0;i<pointVector.size();i++ )
    {
        //Eigen::MatrixXd outcome =
        Eigen::MatrixXd outcome = (rotation*scale)* (*pointVector[i])  + translation;
        //delete  prototypePointVector[i];      // ((rotation*scale)* (*prototypePointVector[i])  + translation).ConvertToPoint();
        MatrixHelper::SetX(*prototypePointVector[i],MatrixHelper::GetX(outcome));
        MatrixHelper::SetY(*prototypePointVector[i],MatrixHelper::GetY(outcome));
        //assosiatedPointIndexVector[i]    = prototypePointVector[i]->associatedTemplateIndex = i;
    }

    return this;
}

и

Eigen::MatrixXd AlgorithmPointBased::UpdateTranslationMatrix( int clusterIndex )
{
    double membershipSum = 0,outcome = 0;
    double currentPower = 0;
    Eigen::MatrixXd outcomePoint = Eigen::MatrixXd(2,1);
    outcomePoint << 0,0;
    Eigen::MatrixXd templatePoint;
    for (int i=0;i< imageDataVector.size();i++)
    {
        currentPower =0; 
        membershipSum += currentPower = pow(membershipMatrix[clusterIndex][i],m);
        outcomePoint.noalias() +=  (*imageDataVector[i] - (prototypeVector[clusterIndex]->rotationMatrix*prototypeVector[clusterIndex]->scalingMatrix* ( *templateCluster->templatePointVector[prototypeVector[clusterIndex]->assosiatedPointIndexVector[i]]) ))*currentPower ;
    }

    outcomePoint.noalias() = outcomePoint/=membershipSum;
    return outcomePoint; //.ConvertToMatrix();
}

Как видите, эти функции выполняют множество операций с матрицей. Вот почему я подумал, что использование Eigen ускорит мое приложение. К сожалению (как я уже упоминал выше), программа работает медленнее.

Есть ли способ ускорить эти функции?

Может быть, если бы я использовал операции с матрицей DirectX, я бы получил лучшую производительность? (однако у меня есть ноутбук со встроенной графической картой).

Ответ 1

Если вы используете типы Eigen MatrixXd, они имеют динамический размер. Вы должны получать намного лучшие результаты от использования типов фиксированного размера, например Matrix4d, Vector4d.

Кроме того, убедитесь, что вы компилируете, чтобы код мог быть векторизован; см. соответствующую документацию Eigen.

Подумайте о том, как использовать материалы библиотеки расширений Direct3D (D3DXMATRIX и т.д.): это нормально (если немного старомодно) для графической геометрии (преобразования 4x4 и т.д.), но это, конечно же, не ускорило GPU (просто старый добрый SSE, я думаю). Также обратите внимание, что это только с плавающей запятой (вы, похоже, настроены на использование удвоений). Лично я бы предпочел использовать Eigen, если бы я не кодировал приложение Direct3D.

Ответ 2

Убедитесь, что включена оптимизация компилятора (например, по крайней мере -O2 на gcc). Eigen сильно шаблонизирован и не будет работать очень хорошо, если вы не включите оптимизацию.

Ответ 3

Вы должны профилировать, а затем сначала оптимизировать алгоритм, а затем реализовать. В частности, опубликованный код довольно неэффективен:

for (int i=0;i<pointVector.size();i++ )
{
   Eigen::MatrixXd outcome = (rotation*scale)* (*pointVector[i])  + translation;

Я не знаю библиотеки, поэтому я даже не буду пытаться угадать количество ненужных временных файлов, которые вы создаете, но простой рефакторинг:

Eigen::MatrixXd tmp = rotation*scale;
for (int i=0;i<pointVector.size();i++ )
{
   Eigen::MatrixXd outcome = tmp*(*pointVector[i])  + translation;

Можно сэкономить немало дорогостоящих умножений (и опять же, возможно, новых временных матриц, которые сразу же отбрасываются.

Ответ 4

Какую версию Eigen вы используете? Недавно они выпустили 3.0.1, который должен быть быстрее, чем 2.x. Кроме того, убедитесь, что вы немного играете с параметрами компилятора. Например, убедитесь, что SSE используется в Visual Studio:

C/С++ → Генерация кода → Включить расширенный набор инструкций

Ответ 5

Несколько пунктов.

  • Почему вы умножаете масштаб вращения внутри цикла, когда этот продукт будет иметь одинаковое значение на каждой итерации? Это очень много усилий.

  • Вы используете матрицы с динамическим размером, а не матрицы с фиксированным размером. Кто-то еще упомянул об этом, и вы сказали, что сбрили 2 секунды.

  • Вы передаете аргументы как вектор указателей на матрицы. Это добавляет дополнительную направленность указателя и уничтожает любую гарантию локальности данных, что даст плохую производительность кеша.

  • Надеюсь, это не оскорбительно, но компилируете ли вы в Release или Debug? Eigen очень медленный в отладочных сборках, потому что он использует множество тривиальных шаблонных функций, которые оптимизированы вне выпуска, но остаются в отладке.

Глядя на ваш код, я не решаюсь обвинять Eigen в проблемах с производительностью. Однако большинство библиотек линейной алгебры (включая Eigen) на самом деле не предназначены для вашего случая использования множества крошечных матриц. В общем случае Eigen будет лучше оптимизирован для матриц размером 100x100 или более. Вы очень хорошо можете лучше использовать свой собственный матричный класс или классы помощника по математике DirectX. Математические классы DirectX полностью независимы от вашей видеокарты.

Ответ 6

Оглядываясь назад на ваш предыдущий пост и код, я предлагаю использовать старый код, но повышаю эффективность, перемещая вещи. Я публикую этот предыдущий вопрос, чтобы оставить ответы отдельно.