この記事は、インテル® デベロッパー・ゾーンに掲載されている「Using HLE and RTM with older compilers with tsx-tools」(http://software.intel.com/en-us/blogs/2013/05/20/using-hle-and-rtm-with-older-compilers-with-tsx-tools) の日本語参考訳です。
HLE/RTM を用いてロックのスケーラビリティーを向上するには、HLE/RTM を有効にする必要があります。glibc (Linux*) のような HLE/RTM 対応のロック・ライブラリーでは、これまでどおりにロックを使用することができ、特別な処理は不要です。ロック・ライブラリーが HLE/RTM に対応していない場合、あるいは独自のロックを使用する場合は、https://software.intel.com/en-us/blogs/2012/11/06/exploring-intel-transactional-synchronization-extensions-with-intel-software で説明されているように HLE/RTM を有効にする必要があります。HLE/RTM は、HLE プリフィックスや RTM 命令で有効にします。gcc 4.8 などの新しいコンパイラーでは、HLE/RTM 向けの組込み関数をサポートしています。
RTM
1 2 3 4 5 6 | #include <immintrin.h> if (_xbegin() == _XBEGIN_START) { /* transaction */ } else { /* fallback path -- take lock */ } </immintrin.h> |
HLE
1 2 3 4 5 6 7 8 9 | while (__atomic_exchange_n(lock, 1 , __ATOMIC_ACQUIRE|__ATOMIC_HLE_ACQUIRE) != 0 ) { int val; /* Wait for lock to become free again before retrying. */ do { _mm_pause(); /* Abort speculation */ __atomic_load_n(lock, &val, __ATOMIC_CONSUME); } while (val == 1 ); } |
古いコンパイラーでは、これらの組込み関数を直接サポートしていませんが、tsx-tools には、gcc および互換 (clang、icc、sun cc など) コンパイラーでこれらの組込み関数を実装する互換ヘッダーが含まれています。そのため、tsx-tools を使用することで、古いコンパイラーでも TSX を利用できます。
rtm.h
標準の TSX 組込み関数 _xbegin()、_xend()、_xtest()、_xabort() を提供します。
hle-official.h
rtm.h と同じです (古い互換名)。
rtm-goto.h
アセンブリーの goto をサポートする gcc 4.6 (Fedora*) または gcc 4.7 以降向けの非公式 RTM 組込み関数の実装です。アボートハンドラーへのジャンプをプログラマーが利用できるようにすることで、トランザクションごとに数命令を節約します。マイクロアーキテクチャーの最適化に役立ちます。
hle-emulation.h
gcc 4.8 以降の HLE 自動組込み関数のエミュレーションです。組込み関数と似ていますが、同じではありません。
gcc 4.8 以降は、HLE を C11 のアトミック組込み関数向けのメモリー・オーダー・モデルとして実装します。gcc のバージョンは、C11 に似ていますが、命名規則が異なります。このメモリーモデルを直接エミュレートすることはできません。
操作は、明示的なメモリーモデル引数なしに __hle_acquire_ と __hle_release_ へマップされます。
別の問題として、C11 のアトミック命令は、引数の多重定義により異なる型をサポートすることが挙げられます。これをエミュレートすることは可能ですが、非常に複雑なマクロが生成されます。そのため、代わりにサフィックスとして型のサイズを追加します。
変更前:
1 | int foo; __atomic_or_fetch(&foo, 1 , __ATOMIC_ACQUIRE|__ATOMIC_HLE_ACQUIRE) |
変更後:
1 | __hle_acquire_or_fetch4(&foo, 1 ); |
さらに、C11 の一部の操作は x86 アトミック命令に直接マップできません。HLE では、1 つの命令が 1 つのトランザクションを開始しなければならないため、これらの操作は省略します。省略される操作には nand、xor、and、or が含まれます。これらの操作は CMPXCHG にマップできますが、スピンループを必要とするため暗黙で行うことは良策とは言えません。HLE load も利用できません。
x86 はすべてのアトミック操作で HLE プリフィックスをサポートしますが、多くの操作はフェッチをサポートしていないため、このスキームですべての操作が生成できるわけではありません。
コンパイラーであれば、フェッチされる値が使用されないことを検出してこれらの操作を生成できるかもしれませんが、ここではそうすることができません。そのため、非 _fetch 操作に対応し、これらの操作では拡張として and、or、xor (ただし nand は除く) もサポートします。
sbb、adc、neg、btr、bts、btc 組込み関数はサポートされません。
いくつかの命令の _n 非汎用バージョンも実装しません。
利用可能な操作
(8 は 64 ビットでのみ有効)
1 2 3 4 5 6 7 8 9 10 | __hle_{acquire,release}_add_fetch{ 1 , 2 , 4 , 8 } __hle_{acquire,release}_sub_fetch{ 1 , 2 , 4 , 8 } __hle_{acquire,release}_fetch_add{ 1 , 2 , 4 , 8 } __hle_{acquire,release}_fetch_sub{ 1 , 2 , 4 , 8 } __hle_{acquire,release}_{add,sub,or,xor,and}{ 1 , 2 , 4 , 8 } (extension) __hle_{acquire,release}_store_n{ 1 , 2 , 4 , 8 } __hle_{acquire,release}_clear{ 1 , 2 , 4 , 8 } __hle_{acquire,release}_exchange_n{ 1 , 2 , 4 , 8 } __hle_{acquire,release}_compare_exchange_n{ 1 , 2 , 4 , 8 } __hle_{acquire,release}_test_and_set{ 1 , 2 , 4 , 8 } (sets to 1 ) |
hle-ms.h
gcc 向けの Microsoft* コンパイラーの HLE 組込み関数エミュレーションです。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。