Comme d' autres l' ont dit, la question est le magasin à l'emplacement de mémoire dans le tableau: x[i][j]
. Voici un peu pourquoi:
Vous disposez d'un tableau bidimensionnel, mais la mémoire de l'ordinateur est intrinsèquement unidimensionnelle. Donc, pendant que vous imaginez votre tableau comme ceci:
0,0 | 0,1 | 0,2 | 0,3
----+-----+-----+----
1,0 | 1,1 | 1,2 | 1,3
----+-----+-----+----
2,0 | 2,1 | 2,2 | 2,3
Votre ordinateur le stocke en mémoire sur une seule ligne:
0,0 | 0,1 | 0,2 | 0,3 | 1,0 | 1,1 | 1,2 | 1,3 | 2,0 | 2,1 | 2,2 | 2,3
Dans le 2ème exemple, vous accédez au tableau en bouclant d'abord sur le 2ème numéro, c'est-à-dire:
x[0][0]
x[0][1]
x[0][2]
x[0][3]
x[1][0] etc...
Cela signifie que vous les frappez tous dans l'ordre. Regardez maintenant la 1ère version. Tu fais:
x[0][0]
x[1][0]
x[2][0]
x[0][1]
x[1][1] etc...
En raison de la façon dont C a disposé le tableau 2D en mémoire, vous lui demandez de sauter partout. Mais maintenant, pour le kicker: Pourquoi est-ce important? Tous les accès à la mémoire sont les mêmes, non?
Non: à cause des caches. Les données de votre mémoire sont transférées vers le CPU en petits morceaux (appelés «lignes de cache»), généralement 64 octets. Si vous avez des entiers de 4 octets, cela signifie que vous obtenez 16 entiers consécutifs dans un petit ensemble soigné. Il est en fait assez lent de récupérer ces morceaux de mémoire; votre processeur peut faire beaucoup de travail dans le temps nécessaire pour charger une seule ligne de cache.
Revenons maintenant à l'ordre des accès: Le deuxième exemple est (1) saisir un morceau de 16 pouces, (2) les modifier tous, (3) répéter 4000 * 4000/16 fois. C'est agréable et rapide, et le CPU a toujours quelque chose à travailler.
Le premier exemple est (1) saisir un morceau de 16 pouces, (2) modifier un seul d'entre eux, (3) répéter 4000 * 4000 fois. Cela va nécessiter 16 fois le nombre de "récupérations" de la mémoire. Votre processeur devra en fait passer du temps à attendre que cette mémoire apparaisse, et pendant qu'il est assis, vous perdez un temps précieux.
Note importante:
Maintenant que vous avez la réponse, voici une note intéressante: il n'y a aucune raison inhérente pour que votre deuxième exemple soit le plus rapide. Par exemple, à Fortran, le premier exemple serait rapide et le second lent. En effet, au lieu d'étendre les choses en "lignes" conceptuelles comme le fait C, Fortran se développe en "colonnes", c'est-à-dire:
0,0 | 1,0 | 2,0 | 0,1 | 1,1 | 2,1 | 0,2 | 1,2 | 2,2 | 0,3 | 1,3 | 2,3
La disposition de C est appelée «ligne majeure» et celle de Fortran est appelée «colonne principale». Comme vous pouvez le voir, il est très important de savoir si votre langage de programmation est majeur en ligne ou en colonne! Voici un lien pour plus d'informations: http://en.wikipedia.org/wiki/Row-major_order