この記事は、インテル® デベロッパー・ゾーンに掲載されている「Transactional Memory Support: the speculative_spin_rw_mutex (Community Preview Feature)」(https://software.intel.com/en-us/blogs/2014/03/07/transactional-memory-support-the-speculative-spin-rw-mutex-community-preview) の日本語参考訳です。
以前の記事 (http://software.intel.com/node/476263) で、新しい世代のプロセッサーでサポートされるインテル® トランザクショナル・シンクロナイゼーション・エクステンション (インテル® TSX) について紹介しました。そこでは、インテル® スレッディング・ビルディング・ブロック(インテル® TBB)の HLE インターフェイス(speculative_spin_mutex)の実装について触れました。この記事では、インテル® TBB 4.2 update 2 のプレビュー機能である speculative_spin_rtw_mutex の実装について説明します。
speculative_spin_rtw_mutex は相互排他に RTM を使用して、同時読み込みと同時書き込みの両方を可能にします。スペキュレーション(投機)なしの操作が必要な場合もあるため、spin_rw_mutex も提供されます。
mutex で保護されたコードの同時実行が競合しない場合、全ての読み込みの完了と書き込みは、明示的なロックなしにアトミックにコミットされます。
もし、競合や投機実行を妨げる他の問題があり、トランザクションがアボートした場合、speculative_spin_rtw_mutex はトランザクションをリトライするか、実際にロックが行われることになります。書き込みが実際にロックされる場合、すべての投機的な読み込みと書き込みは、トランザクションをアボートし、トランザクションを再開するため書き込みの完了を待ちます。また、読み込みが実際にロックされる場合、すべての投機的な書き込みは、トランザクションをアボートし、トランザクションを再開するため読み込みがロックを解放するのを待ちます。実際の読み込みと投機的な読み込みは、並列に処理されます。このすべては、TBB の実装の一部として内部的に処理されます。
speculative_spin_rtw_mutex が通常の spin_rw_mutex を含んでいる理由は、RTM によって完了する保証がないためです。mutex で保護されたコードが、トランザクション内で完了できない操作(システムコールなど)を持っているかもしれません。トランザクション内でアクセスもしくは変更できるキャッシュラインの数には制限があり、その制限に達した場合トランザクションンは完了できません。speculative_spin_rtw_mutex は、トランザクションの試行回数によって制限される処理の進行を保証し、必要であれば非投機的なロックを行います。
前回の記事では、投機的なロックには “フォールバック・パス” (トランザクションが失敗した時に実行できるコードパス)が必要であることを述べました。speculative_spin_rtw_mutex は、同じコードを投機的パスとフォールバック・パスの両方に使用できるように設計されています。これにより、その利用法が大幅に簡素化されます。
トランザクションがアボートした場合、RTM はアボートの理由を示すコードを返します。例えば、スレッドが投機的に実行されている間にタイムスライスが完了した場合、トランザクションはアボートされますが、リトライすると成功することがあります。リターンコードがリトライの成功を示し、リトライの最大回数に達していないなら、トランザクションは再試行されます。
mutex に関しては、いくつか注意すべきことがあります(そのうちいくつかは、speculative_spin_mutex にも適用されます):
- spin_rw_mutex と書き込みフラグは、異なるキャッシュラインにある必要があるため、mutex は 3 つのキャッシュラインを占有し、アロケーターはキャッシュラインの先頭に割り当てることを保証できません。
- アーキテクチャーが RTM をサポートしていない場合、speculative_spin_rtw_mutex は、それらが異なるキャッシュラインにあることを保証するためパディングを行い、spin_rw_mutex がデフォルトに設定されます。
- クラスは、mutex のロックとアンロックのため明示的な提供していません。つまり、プログラムは、speculative_spin_rw_mutex M を定義できず、M.lock() を実行できないことを意味します。speculative_spin_rtw_mutex を使用する適切な方法は、scope_lock によってロックとアンロックを行うことです:
speculative_spin_rw_mutex m; { speculative_spin_rw_mutex::scoped_lock l(m); // mutex で保護されたコード } // ブロックを終了するとき、mutex は scope_lock のため、 // デストラクタ―によってアンロックされます。
これは、スレッドの状態を維持するため各スレッドがローカルストレージをもつ必要があり、スタック上の scope_lock はストレージを含むためです。
- speculative_spin_rtw_mutex は、投機によって書き込みロックを再帰的に取得できるという点で他の実装(pthread の mutex など)とは異なります。書き込みモードで同じロックを再帰的に要求しても、投機の下で実行されなかった限り、デッドロックには陥りません。再帰的ロックがデッドロックに依存するプログラミング・パターンは、特に重要です。この動作に依存する場合、投機的 mutex を使用しないでください。
- インテル® TSX の各実装には、どれだけのスペキュレーション・レベルがサポートされるか制限があります。この制限は世代ごとに代わるかもしれません。
第 4 世代のインテル® Core™ プロセッサーのすべてがトランザクショナル同期をサポートするわけではないことを覚えておいてください。皆さんが使用するプロセッサーでインテル® TSX がサポートされるかどうかを確認するには、ark.intel.com をチェックしてください。インテル® TSX による投機的 mutex をサポートしないプロセッサーでは、非投機的振る舞いによりパフォーマンスが低下する可能性があります。
慎重に性能測定を行うことで、speculative_spin_rtw_mutex がアプリケーションのスケーラビリティーに貢献するか判断するのに役立つでしょう。
インテル® TSX を使用したプログラムの最適化に関しては、Intel® 64 and IA-32 Architectures Optimization Reference Manual の 12 章が役立ちます。日本語の参考訳は、こちらでご覧いただけます。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。