Vectorisation et optimisation
Cette section décrit les éléments suivants :
- Vectorisation : comment Pascal vectorise le code, constructions interdisant la vectorisation
- Optimisation : haut et bas niveau, activation et désactivation de l'optimisation à l'aide de l'instruction d'appel Pascal.
Vectorisation
Le compilateur Pascal convertit certaines boucles FOR en séquences d'opérations vectorielles. Au moment de l'exécution, les boucles FOR vectorisées utilisent les registres vectoriels et les unités fonctionnelles du matériel Cray pour réduire considérablement le temps d'exécution. La vectorisation est activée par l'option de compilation V+, pouvant apparaître sur l'instruction d'appel Pascal ou dans une directive de compilation apparaissant entre les unités de compilation. (La vectorisation est activée par défaut.)
Bien que le compilateur Pascal tente de vectoriser chaque boucle FOR, toutes les boucles FOR ne peuvent pas être vectorisées. Certaines boucles, si elles sont vectorisées, produisent des résultats différents de ceux générés par le code scalaire; d'autres boucles contiennent des opérations ne pouvant pas être effectuées en mode vectoriel. Le compilateur Pascal effectue une analyse détaillée de chaque boucle FOR et ne vectorise pas les boucles pour lesquelles la transformation scalaire en vecteur ne peut pas être garantie en toute sécurité.
Les constructions suivantes inhibent la vectorisation dans une boucle FOR :
- Appels de procédure, y compris les procédures d'entrée/sortie standard telles que READ et WRITE.
- Appels de fonction autres que les appels aux fonctions mathématiques standard.
- Toute instruction autre qu'une instruction d'affectation, une instruction IF ou une instruction composée contenant uniquement des affectations et des instructions IF.
- Vérification des limites du tableau à l'exécution (activée par défaut)
- Tout opérande qui n'entre pas dans l'une des catégories suivantes :
- Une variable n'étant pas affectée dans la boucle :
- Un déréférencement de pointeur (un accès à une variable identifiée par un pointeur) ou un accès à un champ dont la variable de base n'est pas affectée au sein de la boucle :
- Un accès à un élément de tableau dont l'expression d'indice et la variable de base ne sont pas affectées dans la boucle :
- Une expression contenant des opérations sur des constantes et des opérandes non affectés dans la boucle uniquement :
- Un indice de tableau contenant uniquement la variable de contrôle de la boucle FOR et les opérandes constants (et les opérandes invariants si l'option VI est placée dans une directive précédant la boucle) :
- Un déréférencement de pointeur ou un accès à un champ dont la variable de base est une définition de vecteur :
- Un accès à un élément de tableau dont l'expression d'indice est invariante et dont la variable de base est une définition de vecteur :
- Des dépendances vectorielles peuvent exister dans une boucle si la vectorisation effectue l'une des opérations suivantes :
- Écrase un mot de mémoire avant de l'utiliser
- Utilise un mot de mémoire avant d'y stocker une valeur
- Décalage négatif avant un entreposage dans une boucle d'incrémentation :
- Décalage positif après un entreposage dans une boucle incrémentielle :
- Décalage positif avant un entreposage dans une boucle décrémentielle :
- Décalage négatif après un entreposage dans une boucle décrémentielle :
- Les deux définitions de vecteur peuvent faire référence au même tableau.
- Les décalages utilisés dans les expressions d'indice peuvent produire l'une des quatre dépendances décrites précédemment.
Catégorie | Description | |
---|---|---|
Constante | Une constante littérale :
Dans l'exemple précédent, 0,0 est une constante. |
|
Invariant | Un invariant est défini comme suit :
Dans l'exemple précédent, x est l'invariant.
Dans l'exemple précédent, p^ et r.f sont les invariants, car aucun n'est modifié dans la boucle.
Dans l'exemple précédent, b[10] est invariant, car on accède à un élément dont l'indice n'est pas attribué dans la boucle.
Dans l'exemple précédent, x, p^ et (x + p^) sont invariants, car x n'est pas attribué dans la boucle, p^ est un accès de pointeur non attribué dans la boucle et (x+p^) est une expression qui contient uniquement des invariants. |
|
Contrôle de boucle | Une variable de contrôle de boucle FOR :
Dans l'exemple précédent, i est la variable de contrôle de boucle. |
|
Définition d'un vecteur | Une définition d'un vecteur est la suivante :
Dans l'exemple précédent, b[i-10] est une définition vectorielle, car elle contient uniquement la variable de contrôle de boucle FOR (i) et une constante (10).
Dans l'exemple précédent, p[i]^ et r[i].f sont des définitions vectorielles, car p[i]^ est un déréférencement de pointeur et r[i].f est un accès de champ dont la variable de base (r[i]) est une définition vectorielle.
Dans l'exemple précédent, a[i] et p[i] sont des définitions vectorielles, car a[i] est une définition vectorielle (un accès à un élément de tableau avec un indice invariant) et p[i]^ est un déréférencement de pointeur dont la variable de base (p[i]) est une définition vectorielle. |
|
Expression vectorielle | Une expression vectorielle est une expression contenant des opérations sur des opérandes constants, invariants, de définition vectorielle et d'expression vectorielle :
Dans l'exemple précédent, r[i].g est une expression vectorielle car r[i].g est un accès au champ dont la variable de base est une définition vectorielle. |
|
Affectation vectorielle | Une affectation vectorielle est une instruction d'affectation dont le côté gauche est une définition vectorielle et dont le côté droit est une constante, un invariant, une
définition vectorielle ou une expression vectorielle :
Dans l'exemple précédent, r[i].f:=1 est une affectation vectorielle, car r[i].f est une définition vectorielle et 1 est une constante. |
|
Vecteur IF | Un vecteur IF est une instruction IF dont l'expression conditionnelle est une définition de vecteur ou
une expression de vecteur et dont les parties THEN et ELSE contiennent uniquement des affectations de vecteur :
| |
Réduction | Une réduction est une instruction d'affectation étant la seule instruction de la boucle et ayant la forme suivante :
op est l'une des suivantes : +, *, -, AND ou OR exp est une expression vectorielle ou une définition vectorielle
|
|
Recherche | La recherche est une instruction IF dont la condition est une expression vectorielle ou une définition vectorielle contenant uniquement une
instruction GOTO :
|
Les dépendances vectorielles apparaissent de quatre manières :
Pascal analyse chaque instruction FOR pour détecter les dépendances explicites ou potentielles avant de vectoriser la boucle, sauf si l'option du compilateur VI est utilisée dans un commentaire précédant immédiatement la boucle.
Les dépendances sont détectées en comparant chaque définition de vecteur du côté gauche d'une instruction d'affectation à chaque autre définition de vecteur dans la boucle. Une dépendance existe si les conditions suivantes sont réunies :
Pascal suppose qu'une dépendance peut exister si les indices ou les variables de base sont ambigus. S'il existe une possibilité qu'une dépendance existe, Pascal ne vectorise pas une instruction FOR. Pascal ne vectorise pas les instructions FOR suivantes, car des dépendances peuvent exister dans les boucles :
Exemple :
- VAR
- a:ARRAY[1..n] OF REAL;
- i,n:INTEGER;
- x:REAL;
- p:ARRAY[1..n] OF INTEGER;
- r:ARRAY[1..n] OF RECORD
- f,g:INTEGER;
- END;
- BEGIN
- FOR i:=1 TO n DO (* Ligne 1 *)
- BEGIN (* Ligne 2 *)
- a[i]:=100.0; (* Ligne 3 *)
- IF P[i]=NIL THEN (* Ligne 4 *)
- r[i].f:=r[1].g (* Ligne 5 *)
- ELSE (* Ligne 6 *)
- r[i].f:=r[i].f*p[i]^ (* Ligne 7 *)
- END; (* Ligne 8 *)
- END;
Dans l'exemple précédent, les opérandes de la boucle sont classés comme suit :
Ligne | Numéro Classe | Opérande |
---|---|---|
1 | Contrôle de boucle | i |
2 | Constante | 100.0 |
3 | Définition vectorielle | a[i] |
3 | Affectation vectorielle | a[i]:=100.0 |
4 | Définition vectorielle | P[i] |
4 | Expression vectorielle | P[i]=NIL |
4-7 | Vecteur IF | IF P[i]=NIL THEN r[i].f:=r[1].g ELSE r[i].f:=r[i].f*p[i]^ |
5 | Constante | 1 |
5 | Invariante | r[1].g |
5 | Définition vectorielle | r[i].f |
5 | Affectation vectorielle | r[i].f:=r[1].g |
7 | Définition vectorielle | p[i]^ |
7 | Définition vectorielle | r[i].f |
7 | Expression vectorielle | r[i].f*p[i]^ |
7 | Affectation vectorielle | r[i].f:=r[i].f*P[i]^ |
Les boucles FOR contenant des instructions IF, des indices de valeur de tableau (adressage indirect de tableaux) et un déréférencement de pointeur de valeur de tableau ne vectorisent que si l'attribut CIGS (spécifiant l'index compacté et le regroupement/la dispersion) est présent sur le paramètre CPU de l'instruction d'invocation Pascal, soit explicitement, soit en tant qu'attribut par défaut de la machine cible.
Si la directive VI est présente dans un commentaire avant une instruction FOR, Pascal ignore les indices ambigus et autres dépendances potentielles lors de l'analyse de la boucle.
Les exemples de boucles FOR vectorisables suivant supposent ces déclarations :
Exemple 1, une boucle FOR vectorisable :
Exemple 2, une boucle FOR décalant les éléments d'un tableau :
Exemple 3, une boucle FOR produisant le produit scalaire de deux vecteurs :
Exemple 4, adressage complexe :
Exemple 5, indexation indirecte nécessitant du matériel de collecte/diffusion pour la vectorisation :
Exemple 6, division sûre :
Exemple 7, une instruction IF nécessitant du matériel de collecte/diffusion pour la vectorisation :
L'exemple 8, le programme Sieve, est un exemple de programme avec plusieurs boucles vectorisables :
- PROGRAM sieve(OUTPUT);
-
- CONST
- size=1000000;
-
- TYPE
- flagarray=array[2..size] OF BOOLEAN;
-
- VAR
- flags:flagarray;
- i,j:integer;
-
- BEGIN
- FOR i:=2 TO size DO flags[i]:=TRUE;
- FOR i:=2 TO TRUNC(SQRT(size)) DO IF flags[i] THEN FOR j:=i+i TO size BY i DO flags[j]:=FALSE;
- j:0;
- FOR i:=2 TO size DO j:=j+ORD(flags[i]);
- WRITELN('Fin du tamis Pascal vectorisé ;',j:0,' nombres premiers trouvés.')
- END.
Optimisation
Les optimisations de haut niveau sont effectuées avant la génération du code pour un programme Pascal. Les types d'optimisations suivants sont considérés comme des optimisations de haut niveau :
- Propagation des définitions de variables
- Élimination des sous-expressions communes
- Évaluation des expressions au moment de la compilation
- Prédiction de la résidence des registres
- Détection des expressions invariantes de boucle
Les optimisations de bas niveau sont effectuées après la génération du code. Les types d'optimisations suivants sont considérés comme des optimisations de bas niveau :
- Réorganisation des instructions (ordonnancement)
- Élimination des instructions mortes
- Élimination du transfert de registre
Toutes les optimisations sont contrôlées par l'option de compilateur O. Les optimisations sont activées par O+ et désactivées par O-. Étant donné que les optimisations réduisent le temps d'exécution tout en augmentant le temps de compilation, utilisez O- lors du développement d'un programme et O+ pour les exécutions de production.
L'option de compilateur O peut être suivie d'un numéro pour affiner le planificateur d'instructions; l'option O+ est équivalente à l'option O3. Le nombre détermine le temps que le planificateur utilise pour rechercher la planification optimale des instructions. Le nombre spécifié a un effet direct sur le temps nécessaire à la compilation : les nombres élevés augmentent le temps de compilation et les petits nombres le diminuent.
Bien que les petits nombres réduisent le temps de compilation, la spécification d'un nombre trop petit peut entraîner une mauvaise planification du code. L'effet de la planification des instructions varie d'un programme Pascal à l'autre. Il peut être nécessaire d'expérimenter différentes valeurs numériques dans votre programme pour produire le meilleur compromis entre le temps de compilation et le temps d'exécution.