Contenu principal

Cette page a été traduite par traduction automatique. Cliquez ici pour voir la dernière version en anglais.

Boucles imbriquées parfor et for et autres exigences parfor

Boucles parfor imbriquées

Vous ne pouvez pas utiliser une boucle parfor à l'intérieur d'une autre boucle parfor. À titre d’exemple, l’imbrication suivante de boucles parfor n’est pas autorisée :

parfor i = 1:10
    parfor j = 1:5
        ...
    end
end

Conseil

Vous ne pouvez pas imbriquer parfor directement dans une autre boucle parfor. Une boucle parfor peut appeler une fonction qui contient une boucle parfor, mais vous n'obtenez aucun parallélisme supplémentaire.

L'analyseur de code dans l'éditeur MATLAB® signale l'utilisation de parfor dans une autre boucle parfor :

Code analyzer warning stating that PARFOR or SMPD cannot be used inside another PARFOR-loop.

Vous ne pouvez pas imbriquer des boucles parfor car la parallélisation ne peut être effectuée qu'à un seul niveau. Choisissez donc la boucle à exécuter en parallèle et convertissez l’autre boucle en boucle for.

Tenez compte des problèmes de performances suivants lorsque vous traitez des boucles imbriquées :

  • Le traitement parallèle entraîne des frais généraux. En règle générale, vous devez exécuter la boucle externe en parallèle, car la surcharge ne se produit qu'une seule fois. Si vous exécutez la boucle interne en parallèle, chacune des multiples exécutions parfor entraîne une surcharge. Voir Convertir les boucles imbriquées for en boucles parfor pour un exemple de mesure de la surcharge parallèle.

  • Assurez-vous que le nombre d'itérations dépasse le nombre de workers. Sinon, vous n'utilisez pas tous les workers disponibles.

  • Essayez d'équilibrer les temps d'itération de la boucle parfor. parfor essaie de compenser un certain déséquilibre de charge.

Conseil

Exécutez toujours la boucle la plus externe en parallèle, car vous réduisez la surcharge parallèle.

Vous pouvez également utiliser une fonction qui utilise parfor et l'intégrer dans une boucle parfor. La parallélisation se produit uniquement au niveau externe. Dans l'exemple suivant, appelez une fonction MyFun.m à l'intérieur de la boucle externe parfor. La boucle interne parfor intégrée dans MyFun.m s'exécute de manière séquentielle et non en parallèle.

parfor i = 1:10
    MyFun(i)
end

function MyFun(i)
    parfor j = 1:5
        ...
    end
end

Conseil

Les boucles parfor imbriquées ne vous apportent généralement aucun avantage informatique.

Convertir les boucles imbriquées for en boucles parfor

Une utilisation typique des boucles imbriquées consiste à parcourir un tableau en utilisant une variable à une boucle pour indexer une dimension et une variable à boucle imbriquée pour indexer une autre dimension. La forme de base est :

X = zeros(n,m);
for a = 1:n
    for b = 1:m
        X(a,b) = fun(a,b)
    end
end

Le code suivant montre un exemple simple. Utilisez tic et toc pour mesurer le temps de calcul nécessaire.

A = 100;
tic
for i = 1:100
    for j = 1:100
        a(i,j) = max(abs(eig(rand(A))));
    end
end
toc
Elapsed time is 49.376732 seconds.

Vous pouvez paralléliser l’une ou l’autre des boucles imbriquées, mais vous ne pouvez pas exécuter les deux en parallèle. La raison est que les workers d’un pool parallèle ne peuvent pas démarrer ou accéder à d’autres pools parallèles.

Si la boucle comptée par i est convertie en boucle parfor, alors chaque worker du pool exécute les boucles imbriquées à l'aide du compteur de boucles j. Les boucles j elles-mêmes ne peuvent pas s'exécuter en tant que parfor sur chaque worker.

Étant donné que le traitement parallèle entraîne une surcharge, vous devez choisir avec soin si vous souhaitez convertir la boucle for interne ou externe en boucle parfor. L'exemple suivant montre comment mesurer la surcharge parallèle.

Convertissez d’abord uniquement la boucle for externe en boucle parfor. Utilisez tic et toc pour mesurer le temps de calcul nécessaire. Utilisez ticBytes et tocBytes pour mesurer la quantité de données transférées vers et depuis les workers du pool parallèle.

Exécutez le nouveau code, puis exécutez-le à nouveau. La première exécution est plus lente que les exécutions suivantes, car le pool parallèle prend un certain temps pour démarrer et rendre le code disponible pour les workers.

A = 100;
tic
ticBytes(gcp);
parfor i = 1:100
    for j = 1:100
        a(i,j) = max(abs(eig(rand(A))));
    end
end
tocBytes(gcp)
toc
             BytesSentToWorkers    BytesReceivedFromWorkers
             __________________    ________________________

    1             32984                 24512              
    2             33784                 25312              
    3             33784                 25312              
    4             34584                 26112              
    Total    1.3514e+05            1.0125e+05              

Elapsed time is 14.130674 seconds.

Convertissez ensuite uniquement la boucle interne en boucle parfor. Mesurez le temps nécessaire et les données transférées comme dans le cas précédent.

A = 100;
tic
ticBytes(gcp);
for i = 1:100
    parfor j = 1:100
        a(i,j) = max(abs(eig(rand(A))));
    end
end
tocBytes(gcp)
toc
             BytesSentToWorkers    BytesReceivedFromWorkers
             __________________    ________________________

    1        1.3496e+06             5.487e+05              
    2        1.3496e+06            5.4858e+05              
    3        1.3677e+06            5.6034e+05              
    4        1.3476e+06            5.4717e+05              
    Total    5.4144e+06            2.2048e+06              

Elapsed time is 48.631737 seconds.

Si vous convertissez la boucle interne en boucle parfor, le temps et la quantité de données transférées sont bien plus importants que dans la boucle externe parallèle. Dans ce cas, le temps écoulé est presque le même que dans l’exemple de boucle imbriquée for. L'accélération est plus faible que l'exécution de la boucle externe en parallèle, car vous avez plus de transfert de données et donc plus de surcharge parallèle. Par conséquent, si vous exécutez la boucle interne en parallèle, vous n'obtenez aucun avantage informatique par rapport à l'exécution de la boucle série for.

Si vous souhaitez réduire la surcharge parallèle et accélérer votre calcul, exécutez la boucle externe en parallèle.

Si vous convertissez plutôt la boucle interne, chaque itération de la boucle externe lance une boucle parfor distincte. Autrement dit, la conversion de boucle interne crée 100 boucles parfor. Chacune des multiples exécutions parfor entraîne une surcharge. Si vous souhaitez réduire la surcharge parallèle, vous devez plutôt exécuter la boucle externe en parallèle, car la surcharge ne se produit qu'une seule fois.

Conseil

Si vous souhaitez accélérer votre code, exécutez toujours la boucle externe en parallèle, car vous réduisez la surcharge parallèle.

Boucles for imbriquées : exigences et limites

Si vous souhaitez convertir une boucle for imbriquée en boucle parfor, vous devez vous assurer que vos variables de boucle sont correctement classées, voir Dépannage des variables dans les boucles parfor . Si votre code ne respecte pas les directives et restrictions étiquetées comme Obligatoire, vous obtenez une erreur. MATLAB détecte certaines de ces erreurs au moment où il lit le code. Ces erreurs sont étiquetées comme Obligatoire (statique).

Obligatoire (statique) : Vous devez définir la plage d'une boucle for imbriquée dans une boucle parfor par des nombres constants ou des variables de diffusion.

Dans l'exemple suivant, le code de gauche ne fonctionne pas car vous définissez la limite supérieure de la boucle for par un appel de fonction. Le code de droite fournit une solution de contournement en définissant d'abord une variable de diffusion ou constante en dehors de la boucle parfor :

InvalideValide
A = zeros(100, 200);
parfor i = 1:size(A, 1)
    for j = 1:size(A, 2)
        A(i, j) = i + j;
    end
end
A = zeros(100, 200);
n = size(A, 2);
parfor i = 1:size(A,1)
    for j = 1:n
        A(i, j) = i + j;
    end
end
Obligatoire (statique) : La variable d'index pour la boucle imbriquée for ne doit jamais être explicitement attribuée autrement que par son instruction for.

Le respect de cette restriction est obligatoire. Si la variable de boucle for imbriquée est modifiée n'importe où dans une boucle parfor autrement que par son instruction for, la région indexée par la variable de boucle for n'est pas garantie d'être disponible à chaque worker.

Le code de gauche n'est pas valide car il tente de modifier la valeur de la variable de boucle for imbriquée j dans le corps de la boucle. Le code de droite fournit une solution de contournement en affectant la variable de boucle imbriquée for à une variable temporaire t, puis en mettant à jour t.

InvalideValide
A = zeros(10); 
parfor i = 1:10 
    for j = 1:10 
        A(i, j) = 1; 
        j = j+1; 
    end
end
A = zeros(10); 
parfor i = 1:10 
    for j = 1:10 
        A(i, j) = 1;  
        t = j;
        t = t + 1;
    end
end
Obligatoire (statique) : Vous ne pouvez pas indexer ou souscrire une variable de boucle for imbriquée.

Le respect de cette restriction est obligatoire. Si une variable de boucle for imbriquée est indexée, il n'est pas garanti que les itérations soient indépendantes.

L'exemple de gauche n'est pas valide car il tente d'indexer la variable de boucle for imbriquée j . L'exemple de droite supprime cette indexation.

InvalideValide
A = zeros(10); 
parfor i = 1:10 
    for j = 1:10 
        j(1);
    end
end
A = zeros(10); 
parfor i = 1:10 
    for j = 1:10 
        j;
    end
end
Obligatoire (statique) : Lorsque vous utilisez la variable de boucle imbriquée for pour indexer un tableau découpé en tranches, vous devez utiliser la variable sous forme simple et non dans le cadre d'une expression.

Par exemple, le code suivant à gauche ne fonctionne pas, mais celui de droite fonctionne :

InvalideValide
A = zeros(4, 11);
parfor i = 1:4
    for j = 1:10
        A(i, j + 1) = i + j;
    end
end
A = zeros(4, 11);
parfor i = 1:4
    for j = 2:11
        A(i, j) = i + j - 1;
    end
end
Obligatoire (statique) : Si vous utilisez une boucle for imbriquée pour indexer dans un tableau découpé, vous ne pouvez pas utiliser ce tableau ailleurs dans la boucle parfor.

Dans l'exemple suivant, le code de gauche ne fonctionne pas car A est découpé et indexé à l'intérieur de la boucle for imbriquée. Le code de droite fonctionne car v est attribué à A en dehors de la boucle imbriquée :

InvalideValide
A = zeros(4, 10);
parfor i = 1:4
    for j = 1:10
        A(i, j) = i + j;
    end
    disp(A(i, j))
end
A = zeros(4, 10);
parfor i = 1:4
    v = zeros(1, 10);
    for j = 1:10
        v(j) = i + j;
    end
    disp(v(j))
    A(i, :) = v;
end

Limitations de la boucle parfor

Fonctions imbriquées

Le corps d'une boucle parfor ne peut pas référencer une fonction imbriquée. Cependant, il peut appeler une fonction imbriquée par un handle de fonction. Essayez l’exemple suivant. Notez que A(idx) = nfcn(idx) dans la boucle parfor ne fonctionne pas. Vous devez utiliser feval pour appeler le handle fcn dans le corps de la boucle parfor.

function A = pfeg
    function out = nfcn(in)
        out = 1 + in;
    end
    
    fcn = @nfcn;
    
    parfor idx = 1:10
        A(idx) = feval(fcn, idx);
    end
end
>> pfeg
Starting parallel pool (parpool) using the 'Processes' profile ... connected to 4 workers.

ans =

     2     3     4     5     6     7     8     9    10    11

Conseil

Si vous utilisez des handles de fonction qui font référence à des fonctions imbriquées dans une boucle parfor, les valeurs des variables à portée externe ne sont pas synchronisées entre les workers.

Boucles parfor imbriquées

Le corps d'une boucle parfor ne peut pas contenir une boucle parfor. Pour plus d'informations, voir Boucles parfor imbriquées.

Instructions spmd imbriquées

Le corps d'une boucle parfor ne peut pas contenir une instruction spmd et une instruction spmd ne peut pas contenir une boucle parfor. La raison est que les workers ne peuvent pas démarrer ou accéder à d'autres pools parallèles.

Déclarations break et return

Le corps d'une boucle parfor ne peut pas contenir d'instructions break ou return. Pensez plutôt à parfeval ou parfevalOnAll, car vous pouvez utiliser cancel sur eux.

Variables globales et persistantes

Le corps d'une boucle parfor ne peut pas contenir de déclarations de variables global ou persistent. La raison est que ces variables ne sont pas synchronisées entre les workers. Vous pouvez utiliser des variables global ou persistent dans les fonctions, mais leur valeur n'est visible que par le worker qui les crée. Au lieu de variables global, il est préférable d'utiliser des arguments de fonction pour partager des valeurs.

Pour en savoir plus sur les exigences variables, voir Dépannage des variables dans les boucles parfor .

Scénarios

Si un script introduit une variable, vous ne pouvez pas appeler ce script à partir d'une boucle parfor ou d'une instruction spmd. La raison est que ce script provoquerait une violation de transparence. Pour plus de détails, voir Ensure Transparency in parfor-Loops or spmd Statements .

Fonctions anonymes

Vous pouvez définir une fonction anonyme à l'intérieur du corps d'une boucle parfor. Cependant, les variables de sortie découpées dans des fonctions anonymes ne sont pas prises en charge. Vous pouvez contourner ce problème en utilisant une variable temporaire pour la variable découpée, comme indiqué dans l'exemple suivant.

x = 1:10;
parfor i=1:10
    temp = x(i);
    anonymousFunction = @() 2*temp;
    x(i) = anonymousFunction() + i;
end
disp(x);

Pour plus d'informations sur les variables découpées, voir Variables découpées en tranches .

Fonctions inputname

L'utilisation de inputname pour renvoyer le nom de la variable d'espace de travail correspondant à un numéro d'argument n'est pas prise en charge dans les boucles parfor. La raison est que les worker parfor n'ont pas accès à l'espace de travail du desktop MATLAB. Pour contourner ce problème, appelez inputname avant parfor, comme indiqué dans l'exemple suivant.

a = 'a';
myFunction(a)

function X = myFunction(a)
    name = inputname(1);
    
    parfor i=1:2
        X(i).(name) = i;
    end
end

Fonctions load

Les syntaxes de load qui n'affectent pas de structure de sortie ne sont pas prises en charge dans les boucles parfor. À l'intérieur de parfor, attribuez toujours la sortie de load à une structure.

Fonctions nargin ou nargout

Les utilisations suivantes ne sont pas prises en charge dans les boucles parfor :

  • Utilisation de nargin ou nargout sans argument de fonction

  • Utiliser narginchk ou nargoutchk pour valider le nombre d'arguments d'entrée ou de sortie dans un appel à la fonction en cours d'exécution

La raison est que les workers n'ont pas accès à l'espace de travail du desktop MATLAB. Pour contourner ce problème, appelez ces fonctions avant parfor, comme indiqué dans l'exemple suivant.

myFunction('a','b')

function X = myFunction(a,b)
    nin = nargin;
    parfor i=1:2
        X(i) = i*nin;
    end
end

Scripts P-Code

Vous pouvez appeler des fichiers de script P-code à partir d'une boucle parfor, mais les scripts P-code ne peuvent pas contenir de boucle parfor. Pour contourner ce problème, utilisez une fonction P-code au lieu d'un script P-code.

Voir aussi

| |

Rubriques