並列プログラミングでのスレッドプーリングの概要
概要
Delphiが提供しているランタイムライブラリ(RTL)の1つとして「並列プログラミングライブラリ」があります。
並列プログラミングライブラリを使用することで、アプリケーションはタスクを並列に動作させ、複数のCPUデバイスやコンピュータでの分散処理を活用することができます。
この機能を活用することによって、従来のコーディングで非常に時間がかかっていた処理時間を大幅に短縮し、パフォーマンスの向上が見込めます。
その代表的な例としてforループなどの従来の処理をTParallel.Forへの置き換えがあります。
詳しくは、「チュートリアル:並列プログラミング ライブラリの for ループを使用する」を参照してください。
並列プログラミングライブラリでは、CPUの負荷に応じて自動的にワーカースレッドが作成されます。
ワーカースレッドを管理しているのがSystem.Threading.TThreadPool
というクラスです。
現在、docwikiの記事にはTThreadPoolに関する情報が少ないため、こちらのサポート記事で補足させていただきます。
解説
TThreadPoolクラスは、並列プログラミングライブラリ(例えば、TParallel)で使用するワーカースレッドを管理しています。 作成されたワーカースレッド数は、TThreadPool.MaxWorkerThreadsプロパティで取得できます。
TThreadPoolのワーカースレッド数を取得するコード例は、以下の通りです。
Delphi:
uses System.Threading;
..
..
var
psize : integer;
begin
psize := TThreadPool.Default.MaxWorkerThreads;
ShowMessage('Pool size = '+IntToStr(psize));
end;
C++Builder:
#include <System.Threading.hpp>
...
...
int psize = TThreadPool::Default->MaxWorkerThreads;
ShowMessage("Pool size=" + IntToStr(psize));
MaxWorkerThreadで取得できる値は、CPUコアの数とコアごとの最大スレッド数から算出されます。
もう少し具体的に説明すると、クラス内部では、以下のように定義されています。
MaxThreadsPerCPU = 25;
..
..
FMaxLimitWorkerThreadCount := TThread.ProcessorCount * MaxThreadsPerCPU;
..
..
function TThreadPool.GetMaxWorkerThreads: Integer;
begin
Result := FMaxLimitWorkerThreadCount;
end;
..
..
つまり、MaxWorkerThreadは、ProcessorCount × MaxThreadsPerCPU(=25)で算出されます。
TThread.ProcessorCountは、実行しているPCのコア数に依存しています。
例えば、実行しているPCのMaxWorkerThreadの値は、
- コア数=2の場合、2×25=50
- コア数=4の場合、4×25=100
- コア数=8の場合、8×25=200
となり、並列プログラミングライブラリでは、コア数が多いほど、ワーカースレッド数の上限値は多く設定され、その範囲内でスレッドをプーリングします。
ただし、闇雲にワーカースレッド数を増やせば良いというわけではありません。
もし実行環境のPCのスペックを見ないで、固定でワーカースレッド数を設定してしまうと、CPU使用率を過度に圧迫してしまい、逆にパフォーマンスの低下を招く恐れがあります。
よって、通常の場合、開発者がスレッドプーリング数を変更する必要はなく、ThreadPoolのデフォルトの設定で使用することをお勧めします。