SVX日記
2008-07-14(Mon) Fedora9、ハーバードチューン
まず、オイラが使っているSSDというか、コンパクトフラッシュの性能を考察する。Linuxを動かしている都合上、Windows用のベンチマークアプリであるCrystalDiskMarkの結果を引用するのはおかしいのだが、同アプリはフラッシュメディアの性能を測る上で事実上の標準となっており、Web上から結果が入手しやすいから仕方ない。
「不動の地雷評価」
……を築いている「A-DATA Speedy 16G」である。Web上にて、以下の結果を発見した。
Sequential Read : 14.871 MB/s
Sequential Write : 7.124 MB/s
Random Read 512KB : 14.885 MB/s
Random Write 512KB : 1.806 MB/s
Random Read 4KB : 7.859 MB/s
Random Write 4KB : 0.018 MB/s
まずは、Read。このコンパクトフラッシュはUltra DMAモードをサポートしていない。最高でMultiword DMAのmode2までだ。このモードでの理論上の最高転送速度は16.6MB/s。よって、Readの15MB/s弱というのは、フラッシュの性能ではなく、帯域の性能による制限ということになる。
しかしながら、Random Read 4KBの7.859 MB/sは、ハードディスクにとっては「ドギモを抜く」性能である。最新の高速HDD「VelociRaptor」でも1MB/sがやっとだからだ。動く部位(ヘッド)のないフラッシュならではのスコアといえよう。
7.859MB/s * 1024 / 4KB = 2012times/s = 0.50ms
秒間2000回シークでき、シークタイムは0.5msである、と。これは、平均シークタイム5ms程度が「物理的限界」であるハードディスクの、軽く10倍の性能である。最底辺のフラッシュでさえだ。恐ろしく高性能といえよう。
確かに、Sequential Readの絶対値は速くはないものの、通常、アプリの立ち上げ等では、HDDがガリガリいうことからも、Random Readが大きくモノをいう。ベッタリと数百メガのムービー等を外部にコピーする場合などを除けば、Read面ではそこそこ十分な性能といえよう。
0.018MB/s * 1024 / 4KB = 4.608times/s
1.806MB/s * 1024 / 512KB = 3.612times/s
4KBの書き込みと512KBの書き込み「回数」には大差がない。量的には100倍以上の差があるのに、である。そこで、もっとドカンと書き込んでいるSequential Writeの結果から、どこまでが「1回」なのかを求めてみる。
7.124MB/s * 1024 / 3.612times/s = 2019.65KB/s
露骨な結果が出た。2048KB。つまり、2MBまでが1回であろう。逆に言うと、一度に2MBを書き込むのがもっとも効率がよく、4KBずつ書き込むと「秒間5ページを書き込むことができない」ということである。たったの5ページ。こいつぁキッツい制限といえよう。
ちょっと脱線するが、チマタでは8GBのコンパクトフラッシュに比較し、16GBのものが性能が低い、という評価が出回っているが、これは構造上、書き込み単位が倍になっているためと思われる。書き込み単位が倍になれば、Random Writeの書き込み速度が半分になるのも道理であろう。
なんにせよ、OSの動作がもっさりとしてしまう要因はココにあるワケだ。通常、処理を行う際には何にをするにもReadが伴う。デバイスがWriteにかかりっきりになっていれば、Readは遅れ、それは処理の遅延、要するにもっさりに直結するのである。
前回も書いたが、Linuxを含む近年のOSではデバイスに対して書き込みを遅延する。いまこの手にデータがなければ一歩も処理を先に進めることができないReadと違い、Writeなんてのは余剰なメモリに溜め込んでおいてヒマな時にやればいいという思想である。
そこで私は、前回「遅延分をジョロジョロと書き込む」改造と、ReadをWriteに優先させまくる性格を持つI/Oスケジューラであるdeadlineを組み合わせ、デバイスがWriteにかかりっきりになる状態を防ぐべく、チューニングを繰り返したのである……んが、結論から言うと、どうにもできなかった。
というのも、Read面ではそこそこ十分な性能といえる15MB/sだが、秒間2回の書き込みがあると、理論上の性能は半分に、秒間4回の書き込みがあると、理論上はゼロに、もっというと、秒間8回の書き込みがあると、2秒間の遅延が発生してしまう。
上述のとおり、デバイスへの要求は「回数」の観点で制限し、帯域を保護すべきだが、オイラが改造を加えたpage-writeback.cのレイヤでは「ページ(4KB)」単位での制御しかできない。だからといって、最低ラインである秒間4回に配慮して、kupdate_max_writeback_pagesに2、dirty_writeback_centisecsに100を指定して毎秒書き込みを指定すると、100MBの書き込みを処理し終えるのに、2時間弱もかかってしまう。
このヘンが判明したあたりで、いーかげん、レスポンスの悪さに嫌気が差してきたので、8GBでx300なコンパクトフラッシュを買い「お金で解決」してしまおうかとも思ったのだが、それはそれで負けを認めるようで悔しい……そこで、新たに別の手段として、ReadデバイスとWriteデバイスを分割するという手を思いついた。
問題は、チョロチョロとしたWriteでさえ、デバイスのRead帯域を大幅に圧迫してしまうため、あらゆるページインが遅延してしまうという問題である。だが、それならば、Writeしがちな領域を別のデバイスに割り振ってしまい、結果的にWriteをゼロにしてしまえばいいのではないか?
ちょっと大げさではあるが、これはハーバードアーキテクチャ化といえるだろう。事実「命令アクセスとデータアクセスを分離することで高速化できる」というのは、ハーバードアーキテクチャの利点のひとつであり、それはまさに今回の狙いそのものである。
具体的には、別途装備してある16GBのSDカードに/varと/tmpを移動してしまう。既にSDカードは/home領域に使っているが、その用途であればOS自体の挙動に伴ってReadが発生する可能性は低い。
早速、シングルユーザモードに落ちて、/varと/tmpをSDカード側に移動する。/varと/tmpを押さえつけるのが目的ではないので、mountのbind機能を使って元の位置にぶら下げる。/etc/fstabには、以下のようなエントリを加えた。
/mnt/mmc/home /home none bind 0 0
/mnt/mmc/tmp /tmp none bind 0 0
/mnt/mmc/var /var none bind 0 0
# vi /etc/sysctl.conf
vm.dirty_ratio = 80
vm.dirty_background_ratio = 50
vm.dirty_writeback_centisecs = 1500
vm.dirty_expire_centisecs = 9000
# sysctl -p
# echo 8192 > /sys/block/mmcblk0/queue/nr_requests
I/Oスケジューラのタイプをdeadlineに変更する。deadlineスケジューラは、Write要求を遅延し、Read要求を優先する性格のスケジューラである。deadlineスケジューラのパラメータであるfifo_batchは、連続する要求をキューに押し込む限界数、writes_starvedは、Write要求を差し置いてRead要求を優先する限界数で、これらは増加した。その他のread_expire, write_expire, front_mergesはデフォルトのままとした。
# echo deadline > /sys/block/mmcblk0/queue/scheduler
# echo 128 > /sys/block/mmcblk0/queue/iosched/fifo_batch
# echo 8 > /sys/block/mmcblk0/queue/iosched/writes_starved
echo 8192 > /sys/block/mmcblk0/queue/nr_requests
echo deadline > /sys/block/mmcblk0/queue/scheduler
echo 128 > /sys/block/mmcblk0/queue/iosched/fifo_batch
echo 8 > /sys/block/mmcblk0/queue/iosched/writes_starved