Come altri hanno già detto, il problema è il negozio alla locazione di memoria nella matrice: x[i][j]
. Ecco un po 'di informazioni sul perché:
Hai un array bidimensionale, ma la memoria nel computer è intrinsecamente monodimensionale. Quindi, mentre immagini il tuo array in questo modo:
0,0 | 0,1 | 0,2 | 0,3
----+-----+-----+----
1,0 | 1,1 | 1,2 | 1,3
----+-----+-----+----
2,0 | 2,1 | 2,2 | 2,3
Il computer lo memorizza in memoria come un'unica riga:
0,0 | 0,1 | 0,2 | 0,3 | 1,0 | 1,1 | 1,2 | 1,3 | 2,0 | 2,1 | 2,2 | 2,3
Nel secondo esempio, si accede alla matrice eseguendo prima il ciclo sul secondo numero, ovvero:
x[0][0]
x[0][1]
x[0][2]
x[0][3]
x[1][0] etc...
Significa che li stai colpendo tutti in ordine. Ora guarda la prima versione. Stai facendo:
x[0][0]
x[1][0]
x[2][0]
x[0][1]
x[1][1] etc...
A causa del modo in cui C ha disposto l'array 2-d in memoria, gli stai chiedendo di saltare dappertutto. Ma ora per il calciatore: perché è importante? Tutti gli accessi alla memoria sono uguali, giusto?
No: a causa delle cache. I dati della tua memoria vengono trasferiti alla CPU in piccoli blocchi (chiamati "linee cache"), in genere 64 byte. Se hai numeri interi a 4 byte, ciò significa che otterrai 16 numeri interi consecutivi in un piccolo pacchetto ordinato. In realtà è abbastanza lento recuperare questi blocchi di memoria; la tua CPU può fare molto lavoro nel tempo impiegato per il caricamento di una singola riga della cache.
Ora guarda indietro all'ordine degli accessi: il secondo esempio è (1) afferrare un pezzo di 16 pollici, (2) modificarli tutti, (3) ripetere 4000 * 4000/16 volte. È bello e veloce e la CPU ha sempre qualcosa su cui lavorare.
Il primo esempio è (1) afferrare un pezzo di 16 pollici, (2) modificarne solo uno, (3) ripetere 4000 * 4000 volte. Richiederà 16 volte il numero di "recuperi" dalla memoria. La tua CPU dovrà effettivamente passare il tempo in attesa che venga visualizzata quella memoria, e mentre è seduto in giro stai perdendo tempo prezioso.
Nota importante:
Ora che hai la risposta, ecco una nota interessante: non c'è motivo intrinseco che il tuo secondo esempio debba essere quello veloce. Ad esempio, in Fortran, il primo esempio sarebbe veloce e il secondo lento. Questo perché invece di espandere le cose in "file" concettuali come fa C, Fortran si espande in "colonne", ovvero:
0,0 | 1,0 | 2,0 | 0,1 | 1,1 | 2,1 | 0,2 | 1,2 | 2,2 | 0,3 | 1,3 | 2,3
Il layout di C si chiama 'row-major' e quello di Fortran si chiama 'column-major'. Come puoi vedere, è molto importante sapere se il tuo linguaggio di programmazione è row-major o column-major! Ecco un link per maggiori informazioni: http://en.wikipedia.org/wiki/Row-major_order