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
:
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
:
Invalide | Valide |
---|---|
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
.
Invalide | Valide |
---|---|
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.
Invalide | Valide |
---|---|
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 :
Invalide | Valide |
---|---|
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 :
Invalide | Valide |
---|---|
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
ounargout
sans argument de fonctionUtiliser
narginchk
ounargoutchk
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
parfor
| parfeval
| parfevalOnAll