Parallélisation et vectorisation automatiques

Le paralléliseur automatique et le vectoriseur automatique sont conçus pour fournir des gains de performance automatiques pour les boucles de votre code.

Paralléliseur automatique

Le commutateur du compilateur /Qpar active la parallélisation automatique des boucles dans votre code. Quand vous spécifiez cet indicateur sans modifier votre code existant, le compilateur évalue le code pour trouver des boucles susceptibles de tirer un bénéfice de la parallélisation. Comme il peut trouver des boucles qui n’effectuent pas un gros travail et qui ne tireront donc pas de bénéfice de la parallélisation, et comme chaque parallélisation non nécessaire peut provoquer la génération d’un pool de threads, de la synchronisation supplémentaire ou d’autres traitements tendant à ralentir les performances au lieu de les améliorer, le compilateur a une approche prudente dans la sélection des boucles qu’il parallélise. Par exemple, considérez l'exemple suivant dans lequel la limite supérieure de la boucle n'est pas connue au moment de la compilation :

void loop_test(int u) {
   for (int i=0; i<u; ++i)
      A[i] = B[i] * C[i];
}

Comme u il peut s’agir d’une petite valeur, le compilateur ne parallélise pas automatiquement cette boucle. Vous pouvez cependant souhaiter qu'elle soit parallélisée, car vous savez que u aura toujours une valeur élevée. Pour activer la parallélisation automatique, spécifiez #pragma boucle(hint_parallel(n)),n se trouve le nombre de threads à paralléliser. Dans l'exemple suivant, le compilateur tentera de paralléliser la boucle entre 8 threads.

void loop_test(int u) {
#pragma loop(hint_parallel(8))
   for (int i=0; i<u; ++i)
      A[i] = B[i] * C[i];
}

Comme pour toutes les directives pragma, la syntaxe __pragma(loop(hint_parallel(n))) pragma alternative est également prise en charge.

Il existe des boucles que le compilateur ne peut pas paralléliser même si vous le souhaitez. Voici un exemple :

#pragma loop(hint_parallel(8))
for (int i=0; i<upper_bound(); ++i)
    A[i] = B[i] * C[i];

La fonction upper_bound() peut changer chaque fois qu'elle est appelée. Étant donné que la limite supérieure ne peut pas être connue, le compilateur peut émettre un message de diagnostic qui explique pourquoi il ne peut pas paralléliser cette boucle. L'exemple suivant montre une boucle qui peut être parallélisée, une boucle qui ne peut pas être parallélisée, la syntaxe du compilateur à utiliser à l'invite de commandes et la sortie du compilateur pour chaque option de ligne de commande :

int A[1000];
void test() {
#pragma loop(hint_parallel(0))
    for (int i=0; i<1000; ++i) {
        A[i] = A[i] + 1;
    }

    for (int i=1000; i<2000; ++i) {
        A[i] = A[i] + 1;
    }
}

La compilation à l'aide de cette commande :

cl d:\myproject\mylooptest.cpp /O2 /Qpar /Qpar-report:1

génère cette sortie :

--- Analyzing function: void __cdecl test(void)
d:\myproject\mytest.cpp(4) : loop parallelized

La compilation à l'aide de cette commande :

cl d:\myproject\mylooptest.cpp /O2 /Qpar /Qpar-report:2

génère cette sortie :

--- Analyzing function: void __cdecl test(void)
d:\myproject\mytest.cpp(4) : loop parallelized
d:\myproject\mytest.cpp(4) : loop not parallelized due to reason '1008'

Notez la différence dans la sortie entre les deux options /Qpar-report (niveau de création de rapports de paralléliseur automatique). /Qpar-report:1 génère des messages du paralléliseur seulement pour les boucles qui sont parallélisées. /Qpar-report:2 génère des messages du paralléliseur pour les parallélisations qui sont effectuées et pour celles qui ne le sont pas.

Pour plus d’informations sur les codes et messages de raison, consultez Vectorizer et Parallelizer Messages.

Vectoriseur automatique

Le vectoriseur automatique analyse les boucles de votre code, et utilise les registres vectoriels et les instructions vectorielles sur l'ordinateur cible pour les exécuter si c'est possible. Ceci peut améliorer les performances de votre code. Le compilateur cible les instructions SSE2, AVX et AVX2 dans les processeurs Intel ou AMD, ou les instructions NEON sur les processeurs ARM, en fonction du commutateur /arch .

Le vectoriseur automatique peut générer des instructions différentes de celles spécifiées par le commutateur /arch. Ces instructions sont surveillées par une vérification à l'exécution, destinée à s'assurer que ce code s'exécute correctement. Par exemple, quand vous compilez /arch:SSE2, des instructions SSE4.2 peuvent être générées. Une vérification à l'exécution contrôle que SSE4.2 est disponible sur le processeur cible et passe à une version non-SSE4.2 de la boucle si le processeur ne prend pas en charge ces instructions.

Par défaut, le vectoriseur automatique est activé. Si vous souhaitez comparer les performances de votre code sous vectorisation, vous pouvez utiliser #pragma boucle(no_vector) pour désactiver la vectorisation d’une boucle donnée.

#pragma loop(no_vector)
for (int i = 0; i < 1000; ++i)
   A[i] = B[i] + C[i];

Comme pour toutes les directives pragma, la syntaxe __pragma(loop(no_vector)) pragma alternative est également prise en charge.

Comme avec le paralléliseur automatique, vous pouvez spécifier l’option de ligne de commande /Qvec-report (niveau de création de rapports de vectoriseur automatique) pour signaler uniquement les boucles vectorisées avec succès (/Qvec-report:1 ou les boucles vectorisées avec succès et sans succès) ./Qvec-report:2

Pour plus d’informations sur les codes et messages de raison, consultez Vectorizer et Parallelizer Messages.

Pour obtenir un exemple montrant comment fonctionne le vectoriseur dans la pratique, consultez Project Austin Part 2 of 6 : Page Curling

Voir aussi

loop
Programmation parallèle dans le code natif
/Qpar (Paralléliseur automatique)
/Qpar-report (Niveau de rapport du paralléliseur automatique)
/Qvec-report (Niveau de rapport du vectoriseur automatique)
Messages du vectoriseur et du paralléliseur