2015-10-27 15 views
11

Questa è una domanda piuttosto teorica, ma sono piuttosto interessato a questo e sarei lieto se qualcuno avesse qualche conoscenza esperta su questo che lui o lei è disposto a condividere.Perché il metodo mean() di Eigens è molto più veloce di sum()?

Ho una matrice di galleggianti con 2000 righe e 600 colonne e voglio sottrarre la media delle colonne di ogni riga. Ho verificato le seguenti due linee e confrontato la loro compressione:

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

ho pensato, mean() non sarebbe fare qualcosa di diverso dal dividendo la somma di ogni colonna per il numero di righe, ma mentre l'esecuzione della prima linea prende 12,3 secondi sul mio computer, la seconda riga finisce in 0,09 secondi.

Sto usando Eigen version 3.2.6, che attualmente è l'ultima versione e le mie matrici sono archiviate in ordine di riga principale.

Qualcuno sa qualcosa degli interni di Eigen che potrebbe spiegare questa enorme differenza di prestazioni?


Edit: Dovrei aggiungere che data nel codice di cui sopra è in realtà di tipo Eigen::Map< Eigen::MatrixXf<Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> > e mappe funzionalità di Eigen in un buffer crudo.


Edit 2: Come suggerito da GuyGreer, fornirò alcuni esempi di codice per riprodurre i miei risultati:

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

Compilare con:

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

Esecuzione del programma risultante senza argomenti, quindi utilizza sum(), impiega 126 secondi sulla mia macchina, mentre è in esecuzione test 1 utilizzando mean() richiede solo 0,03 secondi!


Edit 3: Come si è scoperto (vedi commenti), non è sum() che richiede molto tempo, ma la divisione del vettore risultante dal numero di righe. Quindi la nuova domanda è: perché Eigen impiega più di 2 minuti per dividere un vettore con 1000 colonne per singolo scalare?

+5

Stai eseguendo i due calcoli uno dopo l'altro nella stessa esecuzione? Se è così, potrebbe essere un problema di cache, prova a scambiare l'ordine in giro. – toth

+1

No, li ho testati separatamente. – Callidior

+1

Non ho mai usato Eigen e quindi non sarò in grado di aiutarti, ma penso che sarebbe utile per le persone se fornissi un test end-to-end che possano essere eseguiti dimostrando ciò che stai chiedendo. In questo modo le persone possono controllare meglio se stessi cosa sta succedendo. – SirGuy

risposta

6

qualche modo, sia la riduzione parziale (somma) e la divisione vengono ricalcolati ogni volta perché alcune informazioni fondamentali del costo valutazione della riduzione parziale sono erroneamente perso da operator/ ... esplicitamente valutazione media risolve il problema:

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

Ovviamente questa valutazione dovrebbe essere effettuata da Eigen per te, come stabilito dal changeset 42ab43a. Questa correzione farà parte delle prossime versioni 3.2.7 e 3.3.

+0

Perfetto, grazie! E i pollici in su per il riferimento al changeset. – Callidior

+0

@ Callidior Ti rendi conto che ggael ha commesso il changeset proprio nel momento in cui ha risposto alla tua domanda, sì? Immagino che dovremmo tutti ringraziarti per aver trovato il bug. –

+0

@AviGinsburg Non me ne sono reso conto, in realtà. Grazie per avermi segnalato questo. – Callidior