Swapfile.sys,スケーラビリティ,「多くの場合,最適化は直感に反する」の例

以前も紹介した『コラム セカンド・オピニオン OS小論:OSの構造をもう少し考えてみる』が再びメモリ周りの解説に突入してました.

なぜ Windows に Swapfile.sys が無いか?


では相違点は? というと、



  • Page Fault HandlerはあくまでProcessのWorking setの一部をin/outする。対してSwapperは物理メモリ上にあるProcess全体をin/outする。

  • Page Fault Handlerは、そのPage Faultを発生したProcessのException Handlerから呼び出される形になる。対してSwapperは、対象となるProcessと別のProcessの形で動作し、そのProcessの状態がHibernate→Suspendになった(あるいは逆にSuspend→Hibernateになった)事をトリガーに動作する。

  • Page Faultが扱うPagingの単位はあくまでも1 Page単位である。対してSwappingではProcess単位となる。

  • Page Fault Handlerが実行されるPriorityは、そのPage Faultを起こしたProcessのBase Priorityとなる。対してSwapperとかModified Page Write Request(*1)は、それぞれSYSGENのSWPPRIO及びMPWPRIOにて指定されたPriorityで実施される。


といったあたりだろうか。このあたりでもちょっと触れたが、Windowsは本質的にVMSにおけるSwap Outに該当する処理をなくしたと考えるのが妥当な発想である。だからこそ、WindowsにはPagefile.sysはあってもSwapfile.sysがないのだ。



なるほど,VMS の時代に「スワップアウト」と呼べる仕組みが,Windows NT で削除されているんですな.Microsoft の資料であんまり「スワップアウト」という言葉が出てこないのはなんでかなと思っていましたが,歴史的経緯を考えれば納得です.

デスクトップアプリケーション使用メモリのスケーリング問題


ただ、実際に使ってみるとそれもどうだろう? と思うシーンも、ままある。例えばメモリ2GB位積んだマシンの上でPhotoshop(バージョンは何でもいい。筆者は未だに7.0を使い続けている)を起動しておく。ここでメモリの利用量をかなり大きくとっておこう(Photo01)。さて、この状態でPhotoshopを放置し、暫く(半日くらい)他の処理を延々とやってから、いきなりPhotoshopで処理を始めると、暫くの間は遅くて遅くて使い物にならない筈だ。理由は簡単で、Photoshopを放置しておくと、その間にPhotoshopのProcessに割り当てられたWorking Setががんがん減ってゆき、最終的にはProcess Header+最小限のWoring Setにまで落ち込むからだ。勿論PhotoshopのProcessはPage Faultを連発しまくるから、次第にWorking Setは増えてゆくが、それはPhotoshopが希望するほどに十分ではない。


ここでもちょっと触れたとおり、システム全体の健全性を考え、あるProcessのWorking Set Sizeは序々にしか増えないからだ。結果、Working Set Sizeがある程度まで達するまでの間、Photoshopは非常にスローモーにしか動作しない。どの位スローモーかというと、適当に選択範囲を選んで、自由変形で回転を掛けると、実際に画面が変化するまで数十秒掛かるくらいにスローモーになる(普段は、リアルタイムで画面も変化する)。これがSwappingを行わないWindowsの現実である。



この問題,うちの日記でも何度も取り上げていますが,確かに悲惨です.
そして,昔は悲惨じゃなかったかというと,確かにそこまで悲惨ではなかったのかもしれません.
ページアウトされるデータ量はここ 10 年ちょっとで 1 MB 規模 → 10 MB 規模 → 100 MB 規模 → 1000 MB 規模と 1000 倍程度にまで大きくなってきているわけですが,この間 HDD の読み出し速度はそこまで大きく改善されたわけではありません.荒っぽい見方をすれば,ページインの待ち時間は 10 倍から 100 倍になっていると言うわけです.つまりスケールしていないと.

「多くの場合,最適化は直感に反する」の例

メモリ上にキャッシュしたデータは,読み出すときにはディスク読み出し並に遅いかもしれません.
独自のキャッシュマネージャを実装するプログラムでは,このことを特に強く意識しておいた方が良いように思います.
例えば Firefox は,bfcache と呼ばれる独自のキャッシュシステムが存在します.

Firefox 1.5 ではウェブページ全体をその JavaScript の状態も含めてメモリ内にキャッシュし、1 つのブラウザセッションとして使用します。訪問したページ間の戻る、進むという動作にページのロードが不要になり、JavaScript の状態も保存されます。この機能によってページナビゲーションが非常に高速化します。この機能は bfcache("Back-Forward Cache" のこと)と呼ばれることもあります。このキャッシュ状態はユーザがブラウザを閉じるまで保存されます。

もう問題はお分かりでしょうが,しばしばこの手の「メモリ上へのキャッシュ」は,データの読み取りが常に早く (低レイテンシで) 行えると仮定しています.しかしこれは必ずしも真ではありません.ページアウトされたデータキャッシュへのアクセスは,HDD へのディスクアクセスと同程度の大きなレイテンシを持ちます.ディスク I/O が集中しているときなどは,Web ページを再びダウンロードし,DOM ツリーを構築し直した方が,HDD からのデータの読み出しよりもレスポンスがよいこともあります.
メモリ上へのキャッシュを試みるプログラマは,次のことに注意.

  • ヒット率×ヒット時のメリットをよく考える.それが OS が使用するディスクキャッシュよりも低ければ,メモリの単なる無駄遣いになっていないか? そしてそういう効率の悪いキャッシュデータがワーキングセットから除外されたときに何が起きるか考えてみる.
  • 最悪の場合,「メモリ上のキャッシュ」からの読み出しコストは,ディスクからデータを読み出すのとコストは変わらない.
  • やんごとなき理由で,確実に物理メモリ上にデータを保持し続けたければ,VirtualLock API の使用を検討する.

個人的には,VirtualLock まで使うような最適化はちょっと手間をかけ過ぎかと思います.もちろんそこが主戦場なら労力を惜しむべきではありませんが,あんまり本質的でなければなるべく手間いらずなメカニズムを採用するのがベターかなと.
OS のメモリ管理も時代に合わせて徐々に変化してきていますから,「無茶しやがって」と言われない範囲内で普通のことをやっていれば,多分平均点は得られるんじゃないでしょうか.
その意味では,Windows XP に 2 GB のメモリを積むとかはちょっと「無茶しやがって」な気もする今日この頃.それだけメモリを積むなら,そろそろ Vista がお勧めです.SP1 あたりのクオリティならまあ人に勧められるかなということで.ワーキングセット管理はだいぶ時代に合ってきています.



ある仮想アドレス範囲の内容が,1)ワーキングセットから除外されているか? 2)次回アクセスでページインが発生するか? を事前に調べる API があると便利そうなのですが,そういうのあったかなぁ? 例によってカーネル空間の管理構造体をスキャンしないと分からないのかもしれませんが.