Armadillo inplace_plus значительно медленнее, чем "нормальная" операция плюс

Я пишу программу с Armadillo 4.500.0 и у меня возникают такие вычисления на месте, как s += v * v.t() * q; значительно медленнее, чем эквивалент s = s + v * v.t() * q; где s, v, а также q векторы соответствующего размера.

Когда я запускаю следующий код, оказывается, что встроенная версия в несколько раз медленнее, чем другая версия, на 500 элементов ~ в 480 раз медленнее (от 5,13 до 0,011 с) с агрессивной оптимизацией (-O3 или -Ofast; Apple LLVM версия 6.0). (лязг-600.0.54)).

#include <iostream>
#include <armadillo>
#include <sys/time.h>

using namespace arma;
using namespace std;

#define N_ELEM 500
#define REP 10000

int main(int argc, const char * argv[]) {
    timeval start;
    timeval end;
    double tInplace, tNormal;
    vec s = randu<vec>(N_ELEM);
    vec v = randu<vec>(N_ELEM);
    vec q = randu<vec>(N_ELEM);

    gettimeofday(&start, NULL);

    for(int i = 0; i < REP; ++i) {
        s += v * v.t() * q;
    }

    gettimeofday(&end, NULL);

    tInplace = (end.tv_sec - start.tv_sec + ((end.tv_usec - start.tv_usec) / 1e6));

    gettimeofday(&start, NULL);

    for(int i = 0; i < REP; ++i) {
        s = s + v * v.t() * q;
    }

    gettimeofday(&end, NULL);

    tNormal = (end.tv_sec - start.tv_sec + ((end.tv_usec - start.tv_usec) / 1e6));

    cout << "Inplace: " << tInplace << "; Normal: " << tNormal << " --> " << "Normal is " << tInplace / tNormal << " times faster" << endl;

    return 0;
}

Кто-нибудь может объяснить, почему оператор на месте работает намного хуже, хотя он может использовать уже доступную память, поэтому ему не нужно ничего копировать?

1 ответ

Решение

Положить скобки вокруг v.t() * q решит проблему:

for(int i = 0; i < REP; ++i) {
    s += v * (v.t() * q);
}

Использование скобок заставляет порядок оценки. Выражение (v.t() * q) будет вычислять скаляр (технически матрица 1x1), который затем используется для умножения v вектор. Скобки также помешают v * v.t() превращаясь в явный внешний продукт.

Armadillo может решить эту проблему автоматически при использовании s = s + v * v.t() * q выражением, но оно (в настоящее время) нуждается в большем количестве подсказок при использовании оператора inplace +=,

Другие вопросы по тегам