インテル® C++ コンパイラー 19.0 デベロッパー・ガイドおよびリファレンス
インテル® Cilk™ Plus は古い機能 (非推奨) です。代わりに、OpenMP* またはインテル® TBB を使用してください。詳細は、「インテル® Cilk™ Plus の代わりに OpenMP* またはインテル® TBB を使用するためのアプリケーションの移行」 (英語) を参照してください。
インテル® Cilk™ Plus は古い機能 (非推奨) です。代わりに、OpenMP* またはインテル® TBB を使用してください。詳細は、「インテル® Cilk™ Plus の代わりに OpenMP* またはインテル® TBB を使用するためのアプリケーションの移行」 (英語) を参照してください。
インテル® Cilk™ Plus プログラムのスタックレイアウトは、C および C++ の標準規則と異なります。そのため、バイナリー形式のプログラム実行ファイルを解析するツール (valgrind やコードカバレッジ・ツールなどのメモリーチェッカーを含む) では、インテル® Cilk™ Plus プログラムの並列処理を解析できないことがあります。通常、そのような場合には、ワーカーを 1 にしてプログラムをシリアル実行すると良いでしょう (つまり、CILK_NWORKERS 環境変数を 1 に設定します)。それでも解析できない場合は、インテル® Cilk™ Plus プログラムをシリアル化してから、解析ツールを使用してみてください。
インテル® Cilk™ Plus は古い機能 (非推奨) です。代わりに、OpenMP* またはインテル® TBB を使用してください。詳細は、「インテル® Cilk™ Plus の代わりに OpenMP* またはインテル® TBB を使用するためのアプリケーションの移行」 (英語) を参照してください。
OS スレッドを使用する場合、次の点に注意する必要があります。
ワーカースレッドは OS スレッドである
ランタイムシステムは、ネイティブ OS の機能を使用してワーカースレッドのセットを割り当てます。
インテル® Cilk™ Plus プログラムは常に利用可能なすべてのプロセッサーを 100% 使用するわけではない
インテル® Cilk™ Plus プログラムを実行すると、並列処理がない場合でも、プログラムがシステムのすべてのプロセッサーのリソースを消費しているかのように見えることがあります。特に、Windows* タスク・マネージャーの [パフォーマンス] タブで、1 つのストランドのみ実行している場合に、すべての CPU がビジー状態になることがあります。
実際には、ランタイム・スケジューラーは、CPU をほかのプログラムに譲ります。ただし、プロセッサーを必要としているプログラムがない場合は、スチールする作業を探すために直ちにワーカーが実行され、これにより CPU がビジー状態になります。そのため、プログラムが常にすべてのプロセッサーを使用しているように見えますが、システムやほかのプログラムの妨げとなることはありません。
ネイティブ・スレッド・インターフェイスを使用する場合には注意が必要である
インテル® Cilk™ Plus のストランドは OS スレッドではありません。実行中に、ストランドがワーカー間を移動することはありません。ただし、cilk_spawn、cilk_sync、または cilk_for は 1 つまたは複数のストランドを終了し、1 つまたは複数の新しいストランドを作成するため、これらの文の後にワーカーが変わることがあります。また、特定のストランドを実行するワーカーを制御する方法もありません。
これは、プログラムにいくつかの影響を与えますが、次の点が最も重要です。
Windows* のスレッド・ローカル・ストレージ (TLS) や Linux*/macOS* の Pthread 固有のデータを利用しないでください。これは、OS スレッドは cilk_spawn、cilk_sync、または cilk_for (呼び出された関数を含む) で変わる可能性があるためです。代わりに、前述の holder レデューサーなど、その他のプログラミング手法を使用してください。
OS のロックや mutex は、ロックしたスレッドでないとオブジェクトを解放できないため、cilk_spawn、cilk_sync、または cilk_for 文では使用しないでください。
インテル® Cilk™ Plus は古い機能 (非推奨) です。代わりに、OpenMP* またはインテル® TBB を使用してください。詳細は、「インテル® Cilk™ Plus の代わりに OpenMP* またはインテル® TBB を使用するためのアプリケーションの移行」 (英語) を参照してください。
Microsoft* Foundation Class (MFC) ライブラリーは、クラスラッパーからオブジェクトの GDI ハンドラーをマップするスレッド・ローカル・ストレージに依存します。インテル® Cilk™ Plus のストランドは、どの OS スレッドでも実行できるわけではないため、インテル® Cilk™ Plus を使用する並列コードでは MFC 関数を安全に呼び出すことができません。
通常、MFC ベースのアプリケーションで計算集約的なタスクを実行する方法は 2 つあります。
ユーザー・インターフェイス (UI) スレッドは、計算集約的なタスクを実行する演算スレッドを作成します。演算スレッドは UI スレッドにメッセージを送信して、UI スレッドが UI を更新します。このため、UI スレッドは計算集約的なタスクの実行から解放され、UI 要求に対応できます。
UI スレッドが計算集約的なコードを実行し、直接 UI を更新します。UI 要求は、ときどき "メッセージポンプ" を実行することで処理します。
ランタイムシステムは OS スレッドを切り替えることができるため、インテル® Cilk™ Plus コードはスレッド・ローカル・ストレージに依存する MFC などのコードと分離する必要があります。
MFC プログラムに演算スレッドを追加するには、次の操作を実行します。
OS 機能 (_beginthreadex や AfxBeginThread) を使用して演算スレッドを作成します。インテル® Cilk™ Plus に変換する C++ コードはすべてこのスレッドで実行する必要があります。演算スレッドによりメイン (UI) スレッドは計算集約的なタスクから解放され、メッセージポンプを実行して、ウィンドウメッセージの処理と UI の更新を行うことができます。
UI ウィンドウのハンドル (HWND) を演算スレッドに渡します。UI の更新が必要になると、演算スレッドは PostMessage を呼び出して UI スレッドにメッセージを送信します。PostMessage はメッセージをマーシャリングして、ウィンドウハンドルを作成したスレッドに関連付けられているメッセージキューに追加します。SendMessage は使用しないでください。SendMessage は、正しい (UI) スレッドではない現在実行中のスレッドで実行されます。
C++ プログラムをテストして、ロジックとスレッド管理が正しいことを確認します。
演算スレッドのロジックにインテル® Cilk™ Plus 構造を追加します。
終了する前に、メイン (UI) スレッドは WaitForSingleObject() を使用して、演算スレッドが完了するのを待機しなければなりません。
製品に含まれる QuickDemo サンプルプログラムは、インテル® Cilk™ Plus アプリケーションで MFC を使用する例を示します。
その他の推奨事項:
メイン UI スレッドが演算スレッドを作成する場合は、演算スレッドの終了を待機すべきではありません。演算スレッドを作成する関数は、メッセージポンプが実行できるように戻る必要があります。
演算スレッドに渡されるデータはすべてスタックに割り当てられているデータであってはなりません。スタックに割り当てられているデータが渡されると、演算スレッドを作成する関数が戻ったときに、スタックデータが解放されてしまいます。
演算スレッドに渡されるデータブロックは、処理が終了し UI に完了メッセージを送信する直前に、演算スレッドによって解放されなければなりません。
演算スレッドを作成する主な理由は、インテル® Cilk™ Plus コードで MFC のスレッドローカル変数を使用しないようにするためなので、CWnd::PostMessage の代わりに PostMessage 関数を使用します。
インテル® Cilk™ Plus 構造を使用する (短い) 計算はブラックボックス化され、メッセージポンプやほかのスレッドと通信しない限り、UI スレッドから直接呼び出すことができます。そのため、非インタラクティブな関数を、それらがインテル® Cilk™ Plus を使用しているかどうかに関わらず、呼び出すことが可能です。