Haswellのトランザクションメモリサポート

前置きが長かったが、Intelの次々世代のプロセサであるHaswellがこのようなトランザクションメモリの実装をサポートするTransaction Synchronization Extensions(TSX)と呼ぶハードウェア機構をサポートしていることが明らかとなった。商用のマイクロプロセサとしては初のトランザクションメモリサポートである。

ハードウェアとしては、トランザクションの開始時点のアーキテクチャ状態をセーブし、Read SetとWrite Setへの他のプロセサからの書き込みアクセスを監視する機構を設けることと、トランザクションの完了まで、Write Setへの書き込みを他のプロセサからは見えないローカルな状態として保持し、トランザクションの完了時点で一括してメモリに書き戻して、他のプロセサから見える状態にすることが必要になる。また、干渉があってトランザクションを中止する場合は、セーブされたアーキテクチャ状態を復元し、Write Setへの書き込みデータを保持するバッファをクリアして、トランザクションの開始直前の状態に戻すことが必要となる。

Haswellでは、従来の粗粒度ロックベースのプログラムの性能を改善するHLE(Hardware Lock Elision)という方法と、一般的なトランザクションメモリの実現方法であるRTM(Restricted Transactional Memory) という方法がサポートされている。

HaswellのHLE

図4に示すように、元となる粗粒度ロックを使うプログラムで、ロック変数を読み、それが"0"ならば"非0"の値を書き込んでロックを得るのに使っているアトミックなXCHGなどの命令にXAQUIREと呼ぶPrefixを付ける。具体的にはXAQUIREは0xF2という値のバイトである。そして、ロックを解放する"0"をストアする命令にはXRELEASEというPrefixを付ける。

図4 粗粒度ロック(左)とHaswellのHLEを使った処理(右)の対応。赤字はハードウェアが行う機能

Haswellでは、このXAQUIRE Prefix付の命令の場合は、ロック変数への書き込みを省略(Elision)してしまう。ただし、そのプロセサで走るプログラムに対しては、ロック変数を読むと書き込まれた値を返す。つまり、内向きのロック変数は書き換えられ、外向きの本当のロック変数は書き換えられていないという二重帳簿の状態にする。

外向きのロック変数は書き換わっていないので、他のプロセサがそのロック変数を読んでロックを獲得することができ、複数のプロセサが並行してトランザクションを実行できる。大部分のケースでは、これらのトランザクションが使用するRead SetやWrite Setに他のプロセサが書き込みを行うことは無く、複数のプロセサで並行して処理ができるので効率が良い。

XAQUIRE付の命令を実行すると、Haswellは汎用レジスタなどのアーキテクチャ状態をバッファにセーブし、それ以降の命令でアクセスするメモリ番地をRead SetとWrite Setに追加しながら他のプロセサからのアクセスを監視するモードに入る。また、Write Setは、内向きには書き込みが行われたように見えるが、外からは、書き込みが行われていないように見える二重帳簿のバッファに格納していく。

そして、Read SetやWrite Setの格納されているキャッシュラインに他のプロセサからのWriteアクセスがあると、干渉があるとみなしてそのトランザクションを中止する。なお、管理がキャッシュライン単位であるので、Read Setで実際に使っているアドレスと他のプロセサのWriteアドレスが異なっていても、同じキャッシュラインに入っている場合は干渉があるとみなされてしまう。これは、本来は並列に実行できるケースでトランザクションを中止させてしまうので性能上の損失にはなるが、安全サイドの処理であり、誤った結果にはならない。

そして、トランザクションの最後のロック変数への"0"の書き込みを行う命令にはXRELEASE Prefix(値は0xF3)を付ける。XRELEASE付の命令を実行する時点で干渉が無かった場合は、Write Setのデータをアトミック(他のプロセサのメモリアクセスを挟まず連続して)にメモリに書き戻し、二重帳簿を解消してすべての書き込まれたデータを他のプロセサから見える状態にする。

そして、トランザクションが中止されると、Write Setを格納したバッファをクリアし、XAQUIREの時点でセーブしたアーキテクチャ状態を復元してトランザクション開始前の状態に戻して、トランザクションを最初から実行する。

ハードウェアがこのような処理を行うことで、データベース全体をロックする粗粒度ロックを使うプログラムのロックの獲得と解放を行う命令にXAQUIREとXRELEASEというPrefixを付けるだけで、細粒度のロックと同じレベルの並列実行が可能となる。