La technique utilisée pour produire cette optimisation est appelée pavage de boucle , également connue sous le nom de blocage de boucle ou exploitation à ciel ouvert et échange .
cache jusqu'à leur réutilisation. Ce partitionnement permet de diviser un grand tableau en blocs plus petits, ce qui adapte les éléments du tableau accédés à la taille du cache, améliorant ainsi sa réutilisation et réduisant les contraintes liées à sa taille.Une boucle ordinaire
Exemple : multiplication matricielle
De nombreuses opérations mathématiques complexes sur ordinateur consacrent une grande partie de leur temps à la multiplication matricielle . L'opération consiste à :
- C = A×B
où A, B et C sont des tableaux N×N. Les indices, pour la description suivante, sont de la forme C[row][column].
La boucle de base est :
La boucle initiale calcule le résultat pour chaque élément de la matrice de résultats un par un. En calculant simultanément un petit bloc d'éléments, la boucle suivante réutilise deux fois chaque valeur chargée, de sorte que la boucle interne effectue quatre chargements et quatre multiplications-additions, résolvant ainsi le problème n° 2. En utilisant simultanément quatre accumulateurs, ce code permet de maintenir un additionneur à virgule flottante unique, d'une latence de 4, occupé presque en permanence (problème n° 1). Cependant, le code ne résout pas le troisième problème. (Il ne traite pas non plus le nettoyage nécessaire lorsque N est impair. Ces détails seront omis dans la suite de la discussion.)
La multiplication matricielle, comme de nombreux autres programmes, peut être limitée par la bande passante mémoire. L'utilisation d'un plus grand nombre de registres permet au compilateur et au programmeur de réduire les besoins en bande passante mémoire. C'est pourquoi les fabricants de processeurs RISC , qui souhaitaient concevoir des machines plus parallèles que les processeurs x86 et 68000 à usage général, ont adopté des fichiers de registres à virgule flottante de 32 entrées .
Le code ci-dessus n'exploite pas efficacement le cache. Lors du calcul d'une bande horizontale de résultats C, une bande horizontale de A est chargée, ainsi que la matrice B entière. Pour l'ensemble du calcul, C est stocké une seule fois (ce qui est optimal), A est chargé une seule fois dans le cache (en supposant qu'une bande de A puisse y être placée avec une bande de B), mais B est chargé N/ib fois, où ib est la taille de la bande dans la matrice C, soit un total de N³ / ib chargements de mots doubles depuis la mémoire principale. Dans le code ci-dessus, ib vaut 2.
L'étape suivante pour réduire le trafic mémoire consiste à maximiser la valeur de `ib`. Celle-ci doit être supérieure à la valeur de « balance » renvoyée par les flux. Dans le cas d'un système Pentium 4 à 2,8 GHz utilisé pour cet exemple, cette valeur est de 16,5. Le deuxième exemple de code ci-dessus ne peut être étendu directement, car cela nécessiterait un nombre beaucoup plus important de registres accumulateurs. C'est pourquoi la boucle est bloquée sur `i`. (Techniquement, il s'agit en fait du deuxième blocage de `i`, le premier étant un blocage par un facteur 2.)