Kernel/VM Advent Calendar 2日目: PV ticketlock ってなんですか><
今日もゆっくり Xen の ML をながめているとこんなスレッドを発見
http://permalink.gmane.org/gmane.comp.emulators.xen.devel/93598
ticket lock? "without expandig spinlock"? lock の一種か? なんじゃこりゃ?
ということで調べてみました。
そもそも spinlock って
spinlock は Linux でよく使われている lock のひとつ。基本的に
- lock とる
- とれなかったらとれるまで spin する (無限ループ)
- なんかやる
- lock 解除
というもので、 1. lock の中身が短く かつ 2. lock の中で sleep しない 場合に使われます。
その実装はアーキテクチャによりますが、 x86 では昔はこのようになっていました。
- lock に使われる数値がある
- 最初は その「lock値」は "1"
- lock 取得時に atomic に「lock値」を 1 減らして、結果が 0 かどうかを見る
- 結果が負であれば spin する
- なんかやる
- 「lock値」を "1" に戻す
そして ticket lock へと
この実装はとても速く、また「lock値」を見てやれば "どれぐらいのスレッドが同時に lock をとろうとしているか" がわかるというメリットがありました。
しかし、ひとつのスレッドの処理が終わった後、次にどのスレッドが起動されるかわからない という「アンフェアな」デメリットがあり、これはレイテンシにも悪影響になります。
lock値 | スレッドA | スレッドB | スレッドC | スレッドD | スレッドE |
1 | - | - | - | - | - |
0 | lock | - | - | - | - |
-1 | working | lock | - | - | - |
-2 | working | spin | lock | - | - |
-3 | working | spin | spin | lock | - |
-4 | working | spin | spin | spin | lock |
1 | unlock | spin | spin | spin | spin |
0 | - | spin | spin | spin | lock |
このように、ずっと待ってたスレッドをさしおいて スレッドEが走ることもありうるわけです。
ということで、これを解消するべく ticket spinlock が導入されました。
ticket spinlock は "Next" と "Owner" という2つの数値を使い、銀行やら役所にある整理券と同じような動きをします。 "Next" が「入口に置いてある整理券」 "Owner" が「窓口で現在処理中の整理券番号の表示」となります。
- 初めは "Next" = "Owner" = 0 で開始
- "Next" を atomic に 1 増やす (整理券をとる)
- 増やす*前*の "Next" == "Owner" かどうか確認 (取った整理券が窓口表示の番号と同じなら窓口へ)
- 違ったら spin しながら 「増やす*前*の "Next" == "Owner"」になるのを待つ (窓口表示を見ながら待ちます)
- なんかする (窓口でいろいろ)
- "Owner" を atomic に 1 増やす (窓口表示が 1 増える)
こうすることで、さっきのケースでも A->B->C->D->E という順番が保証されるようになります。
Next | Owner | スレッドA | スレッドB | スレッドC | スレッドD | スレッドE |
0 | 0 | - | - | - | - | - |
1 | 0 | lock | - | - | - | - |
2 | 0 | working | lock | - | - | - |
3 | 0 | working | spin | lock | - | - |
4 | 0 | working | spin | spin | lock | - |
5 | 0 | working | spin | spin | spin | lock |
5 | 1 | unlock | spin | spin | spin | spin |
5 | 1 | - | lock | spin | spin | spin |
5 | 1 | - | working | spin | spin | spin |
PV spinlock
この spinlock ですが、 VM の中で動いているゲストの spinlock 取得は効率があまりよくなく、特に ticket spinlock とはかなり相性が悪いようです。 (Xen のスケジューラのパフォーマンスに依存してしまう)
http://www.valinux.co.jp/contents/tech/eventreport/0806xensummit.html
特に最近のLinux kernelに取り込まれた「ticket spin lock」と仮想環境とは相性が悪くカーネルコンパイルでCPUサイクルの99%以上がスピンロック取得に無駄に使われ、10sの仕事に45分無駄にされていることになる。いくつかの提案と改善されたベンチマーク結果が示された。
動画: http://vimeo.com/12738341
スライド: http://www.xen.org/files/xensummitboston08/LHP.pdf
と、そこで今では "PV spinlock" という guest 時専用の spinlock 処理で、 guest 上の spinlock を置き換えています。
これは基本的には昔の spinlock と変わりません。違いは
- 2^16 回だけ spin する
- それでも lock がとれなければ、 cpu ごとの event channel に登録して block する (ようするに mutex ぽく sleep するみたいなものでしょう…)
この 「2^16 回」というのは 「98%のlockが取得できる回数」です。 (参考 Thomas Friebel: Xen Summit talk "Preventing Guests from Spinning Around") これによって残り2%の時間を食う lock を寝させて処理の時間を削減できるようになります。
やっと PV ticket lock
さて、ここまでで guest 上の spinlock の問題は解決したわけですが、この実装は「基本的には昔の spinlock と変わらない」のでもちろん昔の spinlock にあった問題が残っていますし、さらに二つの別の問題もあります。
- 物理マシンでの spinlock と実装が異なる
- spinlock 関係の全ての処理に "PV spinlok" をするための処理の overhead が必要
これを変更し
- 上限回数までは ticket spinlock する
- それでも lock できなかった場合に、 Xen に処理を渡し block する
というのが、 "PV ticket lock" になります。 これによって overhead は
- lock をとろうと指定回数だけ spin する処理の部分
- block していた時に block していたものを起こす処理の部分
だけになります。後者はあまり起こらないケースなので、ほとんどの場合 lock をとろうとする時だけ overhead がかかるようになるわけです。
ということで、結構良さげなパッチなのですが……
This code survives for a while with moderate testing, (make -j 100 on
8 VCPUs on a 4 PCPU system), but locks up after about 20 iterations,
so there's still some race/deadlock in there (probably something
misordered), but I think the basic approach is sound.
まだどっかで deadlock してるんかいっ…!
まぁ…今後修正されるでしょう…。 kvm にもメリットありますし期待ですね。