Tutoriel : Utilisation de la boucle For de la bibliothèque de programmation parallèle

De RAD Studio
Aller à : navigation, rechercher

Remonter à Tutoriels Bibliothèque de programmation parallèle

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

  1. 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.
  2. 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 :

Delphi :
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;
C++ :
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;
C++ :

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.
    Forlooptutorial.png

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.

Voir aussi