同時実行ランタイムの概要

更新 : 2010 年 7 月

ここでは、同時実行ランタイムの概要を示します。 また、同時実行ランタイムの利点、使用する状況、コンポーネント同士の対話方法、オペレーティング システムやアプリケーションとの対話方法について説明します。

セクション

このドキュメントは、次のセクションで構成されています。

  • 同時実行ランタイムが重要である理由

  • アーキテクチャ

  • C++ ラムダ式

  • 要件

同時実行ランタイムが重要である理由

同時実行ランタイムでは、同時に実行されるアプリケーションおよびアプリケーション コンポーネントに統一性と予測可能性が提供されます。 同時実行ランタイムの利点の例として、協調タスク スケジューリングと協調ブロッキングの 2 つがあります。

同時実行ランタイムは、ワーク スティーリング アルゴリズムを実装してコンピューティング リソースで作業を効率的に分散する、協調タスク スケジューラを使用します。 たとえば、同じランタイムにより管理される 2 つのスレッドを持つアプリケーションがあるとします。 1 つのスレッドがそのスケジュールされたタスクを完了した場合、他のスレッドから作業をオフロードできます。 この機構により、アプリケーションの作業負荷全体のバランスが保たれます。

同時実行ランタイムは、協調ブロッキングを使用してリソースへのアクセスを同期化する同期プリミティブも提供します。 たとえば、共有リソースへの排他アクセスを必要とするタスクがあるとします。 ランタイムは協調的なブロッキングによって、最初のタスクがリソースを待機するときに残りのクォンタムを使用して別のタスクを実行できます。 この機構により、コンピューティング リソースを最大限に使用できます。

[ページのトップへ]

アーキテクチャ

同時実行ランタイムは 4 つのコンポーネントで構成されます。並列パターン ライブラリ (PPL)、非同期エージェント ライブラリ、タスク スケジューラ、およびリソース マネージャーです。 これらのコンポーネントは、オペレーティング システムとアプリケーションの間に存在します。 次の図は、同時実行ランタイムのコンポーネントがオペレーティング システムとアプリケーションの間でどのようにやり取りするかを示しています。

同時実行ランタイム アーキテクチャ

同時実行ランタイム アーキテクチャ

同時実行ランタイムは非常に組み合わせ自在であり、つまり、既存の機能を結合して機能を拡張できます。 同時実行ランタイムでは、低水準のコンポーネントから並列アルゴリズムなど多数の機能を結合します。

同時実行ランタイムは、協調ブロッキングを使用してリソースへのアクセスを同期化する同期プリミティブも提供します。 これらの同期プリミティブの詳細については、「同期データ構造」を参照してください。

以下のセクションでは、各コンポーネントが備えている機能と使用する場面についての概要を示します。

並列パターン ライブラリ (PPL)

並列パターン ライブラリ (PPL) では、粒度の細かい並列化を実行するための汎用目的コンテナーおよびアルゴリズムを提供します。 PPL では、コンピューティング リソースでコレクションまたはデータのセットに計算を分散する並列アルゴリズムを提供して、命令型データ並列化を実行できます。 また、複数の個別の演算をコンピューティング リソースに分散するタスク オブジェクトを提供することにより、タスクの並列化を行うこともできます。

並列実行の利点を活用できるローカルの計算がある場合は、並列パターン ライブラリを使用します。 たとえば、Concurrency::parallel_for アルゴリズムを使用して、既存の for ループが並列で動作するように指定できます。

並列パターン ライブラリの詳細については、「並列パターン ライブラリ (PPL)」を参照してください。

非同期エージェント ライブラリ

非同期エージェント ライブラリ (または単にエージェント ライブラリ) は、アクターベースのプログラミング モデルと、粒度の粗いデータ フローおよびパイプライン処理タスクの Message Passing Interface の両方の役割を果たします。 非同期エージェントを使用すると、他のコンポーネントがデータを待機するときに作業を実行することにより、待機時間を生産的に活用できます。

相互に非同期通信を行う複数のエンティティがある場合に、エージェント ライブラリを使用します。 たとえば、データをファイルまたはネットワーク接続から読み取って、そのデータを Message Passing Interface で別のエージェントに送信するエージェントを作成できます。

エージェント ライブラリの詳細については、「非同期エージェント ライブラリ」を参照してください。

タスク スケジューラ

タスク スケジューラは、実行時にタスクをスケジュールおよび調整します。 タスク スケジューラは協調的であり、ワーク スティーリング アルゴリズムを使用して処理リソースを最大限活用します。

同時実行ランタイムは既定のスケジューラを提供しているため、インフラストラクチャの詳細を管理する必要はありません。 ただし、アプリケーションの品質ニーズを満たすために、独自のスケジューリング ポリシーを用意すること、または特定のスケジューラを特定のタスクに関連付けることもできます。

タスク スケジューラの詳細については、「タスク スケジューラ (同時実行ランタイム)」を参照してください。

リソース マネージャー

リソース マネージャーの役割は、プロセッサやメモリなどのコンピューティング リソースを管理することです。 リソース マネージャーは、実行時の作業負荷の変更に応答して、最も効果的に使用される場所にリソースを割り当てます。

リソース マネージャーは、コンピューティング リソースの抽象化として機能し、主にタスク スケジューラとやり取りします。 リソース マネージャーを使用してライブラリおよびアプリケーションのパフォーマンスを微調整できますが、通常は、並列パターン ライブラリ、エージェント ライブラリ、およびタスク スケジューラに備わった機能を使用します。 これらのライブラリでは、リソース マネージャーを使用して、作業負荷の変更に応じてリソースのバランスを直接的に再調整します。

[ページのトップへ]

C++ ラムダ式

同時実行ランタイムで定義されている型やアルゴリズムの多くは、C++ テンプレートとして実装されています。 こうした型やアルゴリズムの中には、処理を実行するためのルーチンをパラメーターとして受け取るものが存在します。 このパラメーターには、ラムダ関数、関数オブジェクト、または関数ポインターを使用できます。 これらのエンティティは、処理関数または処理ルーチンとも呼ばれます。

ラムダ式は、Visual C++ 言語の重要な新機能の 1 つです。ラムダ式を使用すると、並列処理用の処理関数を簡潔に定義できます。 関数オブジェクトおよび関数ポインターを使用すると、既存のコードで同時実行ランタイムを使用できます。 ただし、新しいコードを記述するときには、安全性や生産性の面で優れたラムダ式を使用することをお勧めします。

次の例では、Concurrency::parallel_for_each アルゴリズムを複数回呼び出す場合のラムダ関数、関数オブジェクト、および関数ポインターの構文を比較しています。 parallel_for_each を呼び出している各コードは、それぞれ異なる手法を用いて std::array オブジェクト内の各要素の 2 乗を計算しています。

// comparing-work-functions.cpp
// compile with: /EHsc
#include <ppl.h>
#include <array>
#include <iostream>

using namespace Concurrency;
using namespace std;

// Function object (functor) class that computes the square of its input.
template<class Ty>
class SquareFunctor
{
public:
   void operator()(Ty& n) const
   {
      n *= n;
   }
};

// Function that computes the square of its input.
template<class Ty>
void square_function(Ty& n)
{
   n *= n;
}

int wmain()
{
   // Create an array object that contains 5 values.
   array<int, 5> values = { 1, 2, 3, 4, 5 };

   // Use a lambda function, a function object, and a function pointer to 
   // compute the square of each element of the array in parallel.

   // Use a lambda function to square each element.
   parallel_for_each(values.begin(), values.end(), [](int& n){n *= n;});

   // Use a function object (functor) to square each element.
   parallel_for_each(values.begin(), values.end(), SquareFunctor<int>());

   // Use a function pointer to square each element.
   parallel_for_each(values.begin(), values.end(), &square_function<int>);

   // Print each element of the array to the console.
   for_each(values.begin(), values.end(), [](int& n) { 
      wcout << n << endl;
   });
}

この例を実行すると、次の出力が生成されます。

1
256
6561
65536
390625

C++ でのラムダ関数の詳細については、「Lambda Expressions in C++」を参照してください。

[ページのトップへ]

要件

次の表は、同時実行ランタイムの各コンポーネントに関連付けられているヘッダー ファイルを示しています。

コンポーネント

ヘッダー ファイル

並列パターン ライブラリ (PPL)

ppl.h

concurrent_queue.h

concurrent_vector.h

非同期エージェント ライブラリ

agents.h

タスク スケジューラ

concrt.h

リソース マネージャー

concrtrm.h

同時実行ランタイムは、Concurrency 名前空間で宣言されています。 Concurrency::details 名前空間は、同時実行ランタイム フレームワークをサポートしますが、コードから直接使用することを目的としていません。

同時実行ランタイムは、C ランタイム ライブラリ (CRT) の一部として提供されます。 CRT を使用するアプリケーションを構築する方法の詳細については、「C ランタイム ライブラリ」を参照してください。

[ページのトップへ]

履歴の変更

日付

履歴

理由

2010 年 7 月

内容を再構成。

情報の拡充