マルチプロセッサとスケジューラ(その3)

CPUのアーキテクチャが将来はマルチCPU中心に変わっていく。そのための新しい命令が追加されているのだが、まだあまり一般的には広まっていないかも知れない。

本題に入るまえに、まず前回の補足説明をする。Jun Nakajima氏は、intelのSoftware and Solution groupにおいて、Linuxへのハイパースレッド対応を担当していた。WIESS'02(2nd Workshop on Industrial Experiences with Systems Software)におけるテクニカル・セッションのページ

Enhancements for Hyper-Threading Technology in the Operating System: Seeking the Optimal Scheduling

にオペレーティングシステムへのHTの実装に関する詳細なレポートがあるので、参照されたい。

Hyperthread smart "nice" 1

「正しい方法」ではないので、メインラインとしてはお勧めできないが、これは現在私のところでと、P4HTプロセッサを持っている人々のために動作するようになっている。Con Kolivas は、「[PATCH] 2.6.1 Hyperthread smart "nice"」と題したメールでパッチを投稿した。2004年1月末のことである。

我々は以前、SMPモードにおけるハイパースレッド対応のCPU上で、優先度の低いタスクを管理する問題に関するlkmlスレッドを持っていた。

概略は次のような内容である:(訳注:前記事と重なるがオリジナルに含まれている事と、整理のために掲載する)

ユニプロセッサモードにおいてP4HTを実行し、ナイス値+20で(setiathomeのように)cpuに負荷がかかるタスクを実行すれば、そのヘビーな使用期間に得るほとんどのcpuタイムは約8%である。SMPモードにおいてP4HTをブートして、ナイス値+20でcpuに負荷がかかるタスクを実行する場合、たとえ優先度の非常に高いタスクを実行していても、ナイス値-20のタスクを同時に実行すれば、ナイス値+20タスクはcpuタイムの50%しか得られない。とても皮肉なことに、SMPモードでブートすることは、マシンでのバックグラウンドタスクの実行をより遅くしてしまう。

HTベース・パッチと一緒に使うこのパッチは、優先度の低いタスクをスリープさせる代わりに、両方の兄弟(sibling)環境で同時に実行させるためにプライオリティに10以上の差を与えないようにしている。このパッチを備えた、ナイス値0およびナイス値20のタスクを同時に実行すれば、ヘビーな期間のCPU処理能力は、ハイパースレッドの恩恵で全体として10%以内は落ちるが、ナイス値0の仕事は約90%速く走ることになる。異なる「ナイス値」レベルでタスクを実行しなければ、それは効果が無い。それはリアルタイムタスクやカーネル・スレッドの修正をせずに、優先度の高いカーネル・スレッドが兄弟(sibling)CPUの上で走っている間、ナイス設定されたタスクが走ることを可能にすることになる。

ここには、それとともに他のパッチがある。それらは、なぜか少しオフセットがあるが、うまく動作するはずである。

Hyperthread smart "nice" 2

いくつかのパブリックとプライベートの議論が交わされた後、Con Kolivas は、「[PATCH] 2.6.1 Hyperthread smart "nice" 2」と題したメールで言った。

先のパッチに対する批判としては、優先度の高いタスクが走っていた場合に、さらにより多くナイス値設定されたタスクが、兄弟(sibling)CPUの上で走らない問題の解決が寄せられた。このパッチははるかによい解決である。これが行うのは次の通りである。

もし、ナイス値が互いに異なるタスクが、同じCPUの論理コアで動作していれば、より多くナイス値設定されたタスクは、より低くナイス値設定されたタスクとの、タイムスライスの比率で動作する。

例えば、ナイス値19のタスク、および別のコア上で走るナイス値0のタスクが、1つのコア上で走っている場合を想定する。ナイス値0のタスクを連続的に(102ミリ秒は通常のタイムスライスである)走らせるだろう。またナイス値19のタスクは、単に、ナイス値0のタスクが走っている時の最後の10ミリ秒で走るだろう。これははるかにバランスのとれた資源配分をさせる。優先度の高いタスクに重要な先取権を与えるが、それらが両方の論理コア上で走る利益を得ることを可能にさせる。

これは私にとって、ハイパースレッド対ナイス値問題の満足な解決策に見える。再度言うが、これはメインラインのsched.cに対する、あまりにもアーキテクチャに依存する変更である。しかし私がうまく動くと考えるコンセプトの実証によって、その動作を必要とする人々は利用することができる。私のウェブサイト上のものは、他の実験で日々変化している。しかし、添付パッチは「2.6.1」にクリーンに当てはまる。

Ingo Molnarは、「優先度の高いタスクは、それがHTなしで得るのと少なくとも同じ量の生の(共有されない)物理的なCPUスライスを得るだろう」と言うので、この基本的なルールにはとても感動したと伝えた。

monior/mwait

Con Kolivasは同意して貰えて嬉しいと答え、さらにPrescottに追加されるSSE3命令について触れた。

AnandTechのウェブサイトでは、P4 Prescott(新しいPentium4)のハイパースレッドに関する記述として、新しいSSE3命令セットを次のように紹介している。

「ついに我々は2つのスレッドの同期命令 monitormwaitを持つことになる。これらの2つの命令は協力して、ハイパースレッドのパフォーマンスを改善するために作用する。この命令は、コアに送られているスレッドが、OSのアイドルスレッドかデバイスドライバによって生成された他の非生産的なスレッドのいずれかを判断し、その時に処理している、より有用なスレッドすべてを動作させた後で、それらのスレッドについて心配するようにコアに命じるように動作する。」

少なくともインテルは、プライオリティ問題をよく気にしてようには見える。しかし、論理的なコアにわたる全面的なプライオリティ・サポートは期待できない。しかしながら私は、この新しい命令で誰かがコーディングすることができれば、恐らく十分に動作すると推測している。

Ingoは次のように返答した:

これらの命令は、rep-nop(訳注:HTのspinlock同期用にインテルが推奨していたpause命令のこと)の代わりにidle=pollコードの中で使用することができる。この方法、idle-wakeupは、本質的にはメモリバスによって行うことができる。その結果、アイドル・スレッドはCPU時間を浪費しなくなるだろう。(現在のidle=pollはHTボックスに多くのサイクルを浪費するため、利用価値は無い。)

それらは優先度の低いタスクについては、あまり役に立たない。もし、全ての「低プライオリティ・コード」のまわりにmwaitの収穫をまき散らすためにgccを修正しなければ、それほど実用的ではないと私は考えている。

Con Kolivasはその見通しに反対して言った。「暫くの間は、カーネルだけが正確にこういったことをするための、ただ一つの十分賢いものに見える。」

そしてNick Pigginに呼びかけた。 「Nick、スケジュールされた範囲でこのようなものを実装できる見通しはあるかい。それは、特定のアーキテクチャ用にsched.cを私が直接ハックしたのとは異なる、正しい方法になるだろう。」

しかしIngoは返答した。

いや、さらなるハードウェアの助けなしでは、カーネルが「正確に」これをする方法はない。mwaitは、現在の仮想CPUをrep-nopより少しだけ良くサスペンドしている。rep-nopを実行するようにアイドル・ループでは何もしないので、これはアイドル・ループに利用することができる。(恐らくmwaitもスピンロックに使用することができるが、それは別の問題となる。)

ユーザスペースの命令にmwait(あるいはrep-nop)をはさむことなく、「低い優先度」のユーザスペース・コードは、mwaitによって遅くさせることはできない。

これはmwaitによって解決できない問題領域である。コア間のインタラクションに意味がある場合、一旦論理的なコアの数が顕著に増加すれば、仮想CPUにプライオリティを与えることはCPUによって提供されるべきである。SMTの設計は、これは問題とならないところにあるのではないか。

Nick Pigginは、Con Kolivasに対して、それほど難しくないと答えた。基本的に、いくつかのスケジューリングの「へんな癖」を可能にするために領域(domain)にフラグを追加することができる。この場合は、物理的なCPUの中の論理的なコアのバランスを保つ領域(domain)にフラグを足すだろう。その後、cpu_sched_domain(cpu)によって、最も低い領域(domain)を調べることができる。

ここまでのやり取りの後で、カーネルのメンテナであるAndrew Mortonがやっと答えた。

rep-nopを使用したidle=poll の代わりにmwaitを使用したコードは随分前にマージした。arch/i386/kernel/process.c:mwait_idle()を見て欲しい。私は、mwaitを使用したspinlock()のPatchを期待しているのだが、まだ出てきていない。

今後の見通し

monitorとmwaitは実はHT専用の命令ではなく、SMPでの同期にも利用できるもので、Athlon64を始めとするx86-64や他のアーキテクチャのCPUにも取り入れられている。Andrew Mortonが言うようにカーネルのIdleハンドラとしてはすでに入っているが、spinlockにはまだ使われていない。Andrew Mortonが言うように、いずれspinlockにも適用されるのかも知れないが、今後のカーネルやドライバでマルチCPUの同期をとるコードが増えて行くときに、安易にspinlockを多用していいのだろうかという課題はあるだろう。

(6月4日、AnandTechのウェブサイトのリンクを追加)