Tutoriel : Utilisation de la boucle For de la bibliothèque de programmation parallèle
Remonter à Tutoriels Bibliothèque de programmation parallèle
Sommaire
Ce tutoriel explique comment utiliser la méthode TParallel.For de la bibliothèque de programmation parallèle (PPL). TParallel.For divise une boucle for en commandes qui s'exécutent en parallèle et utilise des tâches pour exécuter ces commandes.
Ce tutoriel présente une application simple qui recherche les premiers X
nombres premiers. Ce calcul est effectué avec une version série utilisant la boucle for classique et une version parallèle utilisant la méthode TParallel.For.
Création du projet
Créez un nouveau projet :
- Pour Delphi, choisissez Fichier > Nouveau > Application multi-périphérique - Delphi > Application vide.
- Pour C++Builder, choisissez Fichier > Nouveau > Application multi-périphérique - C++Builder > Application vide.
Ajout des composants
- Ajoutez deux composants TButton à la fiche.
- Définissez la propriété Name de l'un des boutons sur btnForLoop et sa propriété Text sur For Loop. Ce bouton lancera la version série du calcul des nombres premiers.
- Définissez la propriété Name de l'autre bouton sur btnParallelLoop et sa propriété Text sur Parallel Loop. Ce bouton lancera la version parallèle du calcul des nombres premiers.
- Déposez un TMemo sur la fiche.
- Ajustez la taille du TMemo pour afficher les messages entièrement.
Implémentation de la fonctionnalité TParallel.For
Commencez par implémenter les gestionnaires d'événement pour les événements OnClick des boutons For Loop et Parallel Loop. Le bouton For Loop exécute la fonction qui recherche les nombres premiers en utilisant une boucle for classique. Le bouton Parallel Loop utilise la méthode TParallel.For.
Pour cela, écrivez le code comme suit :
const
Max =5000000;
procedure TForm1.btnForLoopClick(Sender: TObject);
var
I, Tot: Integer;
SW: TStopwatch;
begin
// counts the prime numbers below a given value
Tot:=0;
SW := TStopWatch.Create;
SW.Start;
for I := 1 to Max do
begin
if IsPrime(I) then
Inc(Tot);
end;
SW.Stop;
Memo1.Lines.Add(Format('Sequential For loop. Time (in milliseconds): %d - Primes found: %d', [SW.ElapsedMilliseconds,Tot]));
end;
procedure TForm1.btnParallelForClick(Sender: TObject);
var
Tot: Integer;
SW: TStopwatch;
begin
try
// counts the prime numbers below a given value
Tot :=0;
SW :=TStopWatch.Create;
SW.Start;
TParallel.For(2,1,Max,procedure(I:Int64)
begin
if IsPrime(I) then
TInterlocked.Increment(Tot);
end);
SW.Stop;
Memo1.Lines.Add(Format('Parallel For loop. Time (in milliseconds): %d - Primes found: %d', [SW.ElapsedMilliseconds,Tot]));
except on E:EAggregateException do
ShowMessage(E.ToString);
end;
end;
Dans Delphi, la fonction est transmise à TParallel.For en tant que méthode anonyme.
Nous vous conseillons également d'ajouter le code d'implémentation de la fonction IsPrime
:
function IsPrime(N: Integer): Boolean;
var
Test, k: Integer;
begin
if N <= 3 then
IsPrime := N > 1
else if ((N mod 2) = 0) or ((N mod 3) = 0) then
IsPrime := False
else
begin
IsPrime := True;
k := Trunc(Sqrt(N));
Test := 5;
while Test <= k do
begin
if ((N mod Test) = 0) or ((N mod (Test + 2)) = 0) then
begin
IsPrime := False;
break; { jump out of the for loop }
end;
Test := Test + 6;
end;
end;
end;
void __fastcall TForm1::btnForLoopClick(TObject *Sender)
{
int Max=5000000;
Tot =0;
System::Diagnostics::TStopwatch sw= System::Diagnostics::TStopwatch::Create();
sw.Start();
for(int I=1;I<=Max;I++){
if(IsPrime(I)) {
Tot++;
}
}
sw.Stop();
Memo1-> Lines->Add(
String().sprintf(L"Sequential For loop. Time (in milliseconds): %lld, Primes found: %lld",sw.ElapsedMilliseconds, Tot));
}
void __fastcall TForm1::btnParallelForClick(TObject *Sender)
{
int Max=5000000;
Tot =0;
System::Diagnostics::TStopwatch sw= System::Diagnostics::TStopwatch::Create();
sw.Start();
TParallel::For(NULL,1,Max,MyIteratorEvent);
sw.Stop();
Memo1-> Lines->Add(
String().sprintf(L"Parallel For loop. Time (in milliseconds): %lld, Primes found: %lld",sw.ElapsedMilliseconds, Tot));
}
Dans C++, la fonction est transmise à la méthode TParallel.For en tant qu'événement itérateur.
Le code d'implémentation d'un événement itérateur est comme suit :
void __fastcall TForm1::MyIteratorEvent(TObject* Sender, int AIndex)
{
if(IsPrime(AIndex)){
TInterlocked::Increment(Tot);
}
}
Le code pour la fonction IsPrime
dans C++ est :
bool TForm1::IsPrime(int N){
int Test, k;
bool aPrime;
if (N <= 3){
return N > 1;
}
else if (((N % 2) == 0) || ((N % 3) == 0)){
return false;
}else{
aPrime = true;
k = (int)sqrt(N);
Test = 5;
while (Test <= k){
if (((N % Test) == 0) || ((N % (Test + 2)) == 0)){
aPrime = false;
break; //jump out of the for loop
}
Test = Test + 6;
}
return aPrime;
}
}
Ajoutez également les déclarations suivantes au fichier d'en-tête du projet :
private: // User declarations
void __fastcall MyIteratorEvent(TObject* Sender, int AIndex);
public: // User declarations
int Tot;
Incluez également les bibliothèques nécessaires dans le code.
Delphi :
Pour les applications Delphi, ajoutez l'unité suivante à la clause uses, si elle n'est pas présente :
uses
System.Threading;
System.Diagnostics;
System.SyncObjs;
Pour les applications C++, ajoutez l'opérateur include suivant dans le fichier d'en-tête du projet :
#include <System.Threading.hpp>
#include <System.Diagnostics.hpp>
#include <System.SyncObjs.hpp>
Exécution de l'application
A ce stade, vous pouvez exécuter l'application :
- Appuyez sur
F9
ou choisissez Exécuter > Exécuter.
Vous pouvez voir que la méthode TParallel.For est plus efficace car elle utilise les CPU disponibles pour exécuter les procédures en parallèle.