Tutorial: Verwenden der For-Schleife aus der Parallel Programming Library
Nach oben zu Tutorials zur Parallel Programming Library
Inhaltsverzeichnis
In diesem Tutorial wird die Verwendung der Methode TParallel.For aus der Parallel Programming Library (PPL) gezeigt. TParallel.For teilt eine for-Schleife in einzelne Teile auf, die parallel ausgeführt werden, und führt diese Teile mit Aufgaben aus.
Dieses Tutorial besteht aus einer einfachen Anwendung, die die ersten X
Primzahlen sucht. Diese Berechnung wird mit einer seriellen Version mit der klassischen for-Schleife und einer parallelen Version mit der Methode TParallel.For durchgeführt.
Erstellen des Projekts
Erstellen Sie ein neues Projekt:
- Für Delphi wählen Sie Datei > Neu > Geräteübergreifende Anwendung - Delphi > Leere Anwendung.
- Für C++Builder wählen Sie Datei > Neu > Geräteübergreifende Anwendung - C++Builder > Leere Anwendung.
Hinzufügen der Komponenten
- Fügen Sie dem Formular zwei TButton-Komponenten hinzu.
- Setzen Sie die Eigenschaft Name einer Schaltfläche auf btnForLoop und die Eigenschaft Text auf For Loop. Mit dieser Schaltfläche wird die serielle Version der Primzahlberechnung aufgerufen.
- Setzen Sie die Eigenschaft Name der anderen Schaltfläche auf btnParallelLoop und die Eigenschaft Text auf Parallel Loop. Mit dieser Schaltfläche wird die parallele Version der Primzahlberechnung aufgerufen.
- Legen Sie eine TMemo-Komponente auf dem Formular ab.
- Passen Sie die Größe von TMemo so an, dass die gesamten Meldungen angezeigt werden können.
Implementieren der TParallel.For-Funktionalität
Implementieren Sie zuerst die Ereignisbehandlungsroutinen für die OnClick-Ereignisse der Schaltflächen For Loop und Parallel Loop. Mit der Schaltfläche For Loop wird die Funktion ausgeführt, die nach den Primzahlen mit einer klassischen for-Schleife sucht. Parallel Loop verwendet TParallel.For.
Schreiben Sie dazu Code wie den folgenden:
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;
In Delphi wird die Funktion als anonyme Methode an TParallel.For übergeben.
Fügen Sie auch den Implementierungscode für die Funktion IsPrime
hinzu:
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));
}
In C++ wird die Funktion als Iterator-Ereignis an TParallel.For übergeben.
Der Code zur Implementierung des Iterator-Ereignisses:
void __fastcall TForm1::MyIteratorEvent(TObject* Sender, int AIndex)
{
if(IsPrime(AIndex)){
TInterlocked::Increment(Tot);
}
}
Der Code für die Funktion IsPrime
in C++:
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;
}
}
Fügen Sie auch die folgenden Deklarationen in die Projekt-Header-Datei ein:
private: // User declarations
void __fastcall MyIteratorEvent(TObject* Sender, int AIndex);
public: // User declarations
int Tot;
Sie müssen auch die erforderlichen Bibliotheken in den Code einbeziehen.
Delphi:
Fügen Sie für Delphi-Anwendungen der uses-Klausel die folgende Unit hinzu, sofern sie noch nicht darin enthalten ist:
uses
System.Threading;
System.Diagnostics;
System.SyncObjs;
Fügen Sie für C++-Anwendungen der Projekt-Header-Datei den folgenden include-Operator hinzu:
#include <System.Threading.hpp>
#include <System.Diagnostics.hpp>
#include <System.SyncObjs.hpp>
Ausführen der Anwendung
Nun können Sie die Anwendung ausführen:
- Drücken Sie
F9
, oder wählen Sie Start > Start.
Wie Sie sehen, ist die Methode TParallel.For effizienter, weil sie die verfügbaren CPUs für die parallele Ausführung von Prozeduren verwendet.