Questo tipo di domanda è ricorrente e dovrebbe avere una risposta più chiara di "MATLAB utilizza librerie altamente ottimizzate" o "MATLAB utilizza MKL" per una volta su Stack Overflow.
Storia:
La moltiplicazione di matrici (insieme alla moltiplicazione di matrici, vettori di vettori e molte delle scomposizioni di matrici) è (sono) i problemi più importanti dell'algebra lineare. Gli ingegneri hanno risolto questi problemi con i computer sin dai primi giorni.
Non sono un esperto di storia, ma a quanto pare allora, tutti riscrivevano la sua versione FORTRAN con semplici loop. Poi è arrivata una certa standardizzazione, con l'identificazione di "kernel" (routine di base) che la maggior parte dei problemi di algebra lineare necessitavano per essere risolti. Queste operazioni di base sono state quindi standardizzate in una specifica chiamata: Sottoprogrammi di algebra lineare di base (BLAS). Gli ingegneri potrebbero quindi chiamare queste routine BLAS standard ben collaudate nel loro codice, rendendo il loro lavoro molto più semplice.
BLAS:
BLAS si è evoluto dal livello 1 (la prima versione che ha definito operazioni di vettore scalare e vettoriale-vettoriale) al livello 2 (operazioni di matrice vettoriale) al livello 3 (operazioni di matrice-matrice) e ha fornito sempre più "kernel" così standardizzati più e più delle operazioni fondamentali di algebra lineare. Le implementazioni originali FORTRAN 77 sono ancora disponibili sul sito Web di Netlib .
Verso prestazioni migliori:
Quindi nel corso degli anni (in particolare tra le versioni BLAS livello 1 e livello 2: primi anni '80), l'hardware è cambiato, con l'avvento delle operazioni vettoriali e delle gerarchie di cache. Queste evoluzioni hanno permesso di aumentare sostanzialmente le prestazioni delle subroutine BLAS. Diversi fornitori hanno poi seguito l'implementazione delle routine BLAS, sempre più efficienti.
Non conosco tutte le implementazioni storiche (non ero nato o un bambino allora), ma due dei più importanti sono usciti nei primi anni 2000: Intel MKL e GotoBLAS. Matlab utilizza Intel MKL, che è un ottimo BLAS ottimizzato e che spiega le grandi prestazioni che si vedono.
Dettagli tecnici sulla moltiplicazione Matrix:
Allora perché Matlab (MKL) è così veloce dgemm
(moltiplicazione matrice-matrice generale a doppia precisione)? In termini semplici: perché utilizza la vettorializzazione e una buona memorizzazione nella cache dei dati. In termini più complessi: vedi l' articolo fornito da Jonathan Moore.
Fondamentalmente, quando esegui la moltiplicazione nel codice C ++ che hai fornito, non sei affatto favorevole alla cache. Poiché sospetto che tu abbia creato una serie di puntatori per allineare le matrici, i tuoi accessi nel tuo ciclo interno alla colonna k-esima di "matice2": matice2[m][k]
sono molto lenti. Infatti, quando accedi matice2[0][k]
, devi ottenere l'elemento k-esimo dell'array 0 della tua matrice. Quindi nella successiva iterazione, è necessario accedere matice2[1][k]
, che è l'elemento k-esimo di un altro array (l'array 1). Quindi nella prossima iterazione accedi ad un altro array, e così via ... Dato che l'intera matrice matice2
non può stare nella cache più alta (è 8*1024*1024
grande byte), il programma deve recuperare l'elemento desiderato dalla memoria principale, perdendo molto tempo.
Se hai appena trasposto la matrice, in modo che gli accessi si trovassero in indirizzi di memoria contigui, il tuo codice sarebbe già in esecuzione molto più velocemente perché ora il compilatore può caricare intere righe nella cache contemporaneamente. Prova questa versione modificata:
timer.start();
float temp = 0;
//transpose matice2
for (int p = 0; p < rozmer; p++)
{
for (int q = 0; q < rozmer; q++)
{
tempmat[p][q] = matice2[q][p];
}
}
for(int j = 0; j < rozmer; j++)
{
for (int k = 0; k < rozmer; k++)
{
temp = 0;
for (int m = 0; m < rozmer; m++)
{
temp = temp + matice1[j][m] * tempmat[k][m];
}
matice3[j][k] = temp;
}
}
timer.stop();
Quindi puoi vedere come solo la localizzazione della cache ha aumentato in modo sostanziale le prestazioni del tuo codice. Ora le dgemm
implementazioni reali sfruttano questo a un livello molto ampio: eseguono la moltiplicazione su blocchi della matrice definiti dalla dimensione del TLB (Translation lookaside buffer, long story short: che cosa può essere effettivamente memorizzato nella cache), in modo che vengano trasmessi in streaming al processore esattamente la quantità di dati che può elaborare. L'altro aspetto è la vettorializzazione, usano le istruzioni vettorializzate del processore per un throughput ottimale delle istruzioni, cosa che non si può davvero fare dal codice C ++ multipiattaforma.
Infine, le persone che affermano che è a causa dell'algoritmo di Strassen o Coppersmith – Winograd hanno torto, entrambi questi algoritmi non sono implementabili nella pratica, a causa delle considerazioni hardware sopra menzionate.