チュートリアル:並列プログラミング ライブラリの for ループを使用する
並列プログラミング ライブラリ関連のチュートリアル への移動
このチュートリアルでは、並列プログラミング ライブラリ(PPL)の TParallel.For メソッドの使い方を説明します。TParallel.For は、for ループを並列に動作する複数の部分に分割し、タスクを使って個々の部分を実行します。
このチュートリアルでは、最初の X
個の素数を探す簡単なアプリケーションを作成します。この計算は、従来の for ループを使った順次処理と、TParallel.For メソッドを使った並列処理で行います。
プロジェクトの作成
新規プロジェクトを作成します。
- Delphi の場合は、[ファイル|新規作成|マルチデバイス アプリケーション - Delphi|空のアプリケーション]を選択します。
- C++Builder の場合、[ファイル|新規作成|マルチデバイス アプリケーション - C++Builder|空のアプリケーション]を選択します。
コンポーネントの追加
TParallel.For 機能の実装
まず、[For Loop]ボタンおよび[Parallel Loop]ボタンの OnClick イベントのイベント ハンドラを実装します。[For Loop]ボタンは、従来の for ループを使って素数を探す関数を実行するためのものです。[Parallel Loop]では TParallel.For を使用します。
次のコードを記述します。
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;
Delphi では、関数が無名メソッドとして TParallel.For に渡されます。
また、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));
}
C++ では、関数がイテレータ イベントとして TParallel.For に渡されます。
このイテレータ イベントの実装コードは次のとおりです。
void __fastcall TForm1::MyIteratorEvent(TObject* Sender, int AIndex)
{
if(IsPrime(AIndex)){
TInterlocked::Increment(Tot);
}
}
C++ の IsPrime
関数のコードは次のとおりです。
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;
}
}
さらに、次の宣言をプロジェクト ヘッダー ファイルに追加します。
private: // User declarations
void __fastcall MyIteratorEvent(TObject* Sender, int AIndex);
public: // User declarations
int Tot;
また、必要なライブラリをコードに含める必要があります。
Delphi の場合:
Delphi アプリケーションの場合、次のユニットが uses 句に含まれていなければ追加します。
uses
System.Threading;
System.Diagnostics;
System.SyncObjs;
C++ アプリケーションの場合、プロジェクト ヘッダー ファイルに以下の #include を追加します。
#include <System.Threading.hpp>
#include <System.Diagnostics.hpp>
#include <System.SyncObjs.hpp>
アプリケーションの実行
この時点で、アプリケーションを実行することができます。
F9
キーを押すか、[実行|実行]を選択します。
TParallel.For がどれだけ効率がいいかが見てとれます。これは、利用可能な CPU を使って手続きを並列で実行しているためです。