2015-10-27 2 views
11

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

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

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, что может объяснить эту огромную разницу в производительности?


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


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

Compile с:

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

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


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

+5

Вы выполняете два вычисления один за другим в одном и том же пробеге? Если это так, это может быть проблема кэширования, попробуйте поменять порядок. – toth

+1

Нет, я тестировал их отдельно. – Callidior

+1

Я никогда не использовал Eigen и поэтому не смогу вам помочь, но я думаю, что было бы полезно людям, если бы вы предоставили сквозной тест, который они могут запустить, демонстрируя то, о чем вы просите. Таким образом, люди могут лучше проверить себя, что происходит. – SirGuy

ответ

6

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

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

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

+0

Отлично, спасибо! И палец вверх для ссылки на набор изменений. – Callidior

+0

@Callidior Вы понимаете, что ggael совершил набор изменений прямо в то время, когда он ответил на ваш вопрос, да? Думаю, все мы должны поблагодарить вас за обнаружение ошибки. –

+0

@AviGinsburg Я вообще этого не осознавал. Спасибо, что указал мне на это. – Callidior

Смежные вопросы