Почему метод Eigens mean() намного быстрее, чем sum()?

Это довольно теоретический вопрос, но я очень заинтересован в этом и был бы рад, если бы у кого-то были какие-то экспертные знания по этому поводу, которые он или она хочет поделиться.

У меня есть матрица с поплавками с 2000 строками и 600 cols и хочу вычесть среднее значение столбцов из каждой строки. Я тестировал следующие две строки и сравнивал их время выполнения:

MatrixXf centered = data.rowwise() - (data.colwise().sum() / data.cols());
MatrixXf centered = data.rowwise() - data.colwise().mean();

Я думал, что mean() не будет делать что-то отличное от деления суммы каждого столбца на количество строк, но пока выполнение первой строки занимает 12,3 секунды на моем компьютере, вторая строка заканчивается через 0,09 секунды.

Я использую Eigen version 3.2.6, который в настоящее время является последней версией, а мои матрицы хранятся в строчном порядке.

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


Изменить: Я должен добавить, что data в приведенном выше коде фактически имеет тип Eigen::Map< Eigen::MatrixXf<Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> > и отображает функциональность Eigen в необработанный буфер.


Изменить 2: Как было предложено GuyGreer, я приведу несколько примеров кода, чтобы воспроизвести мои выводы:

#include <iostream>
#include <chrono>
#include <Eigen/Core>
using namespace std;
using namespace std::chrono;
using namespace Eigen;

int main(int argc, char * argv[])
{
    MatrixXf data(10000, 1000), centered;
    data.setRandom();
    auto start = high_resolution_clock::now();
    if (argc > 1)
        centered = data.rowwise() - data.colwise().mean();
    else
        centered = data.rowwise() - (data.colwise().sum() / data.rows());
    auto stop = high_resolution_clock::now();
    cout << duration_cast<milliseconds>(stop - start).count() << " ms" << endl;
    return 0;
}

Скомпилировать с помощью:

g++ -O3 -std=c++11 -o test test.cc

Запуск полученной программы без аргументов, так что использует sum(), занимает 126 секунд на моей машине, а запуск test 1 с использованием mean() занимает всего 0,03 секунды!


Изменить 3: Как оказалось (см. комментарии), это не sum() занимает так много времени, но деление результирующего вектора на количество строк. Итак, новый вопрос: почему Eigen занимает более 2 минут, чтобы разделить вектор на 1000 столбцов на один скаляр?

Ответ 1

Как-то, как частичное сокращение (сумма), так и деление пересчитываются каждый раз, потому что некоторая важная информация об оценочной стоимости частичного сокращения ошибочно теряется на operator/... Явная оценка среднего исправляет проблему:

centered = data.rowwise() - (data.colwise().sum() / data.cols()).eval();

Конечно, эта оценка должна быть выполнена Eigen для вас, как исправлено с помощью набора изменений 42ab43a. Это исправление будет частью следующих выпусков 3.2.7 и 3.3.