SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|

2008-07-14(Mon) Fedora9、ハーバードチューン

  「実際のチューニングは明日回し」などとして長々と引っ張りつつ、結局は、ぜんぜん違う結論に至っていたりする。

  まず、オイラが使っているSSDというか、コンパクトフラッシュの性能を考察する。Linuxを動かしている都合上、Windows用のベンチマークアプリであるCrystalDiskMarkの結果を引用するのはおかしいのだが、同アプリはフラッシュメディアの性能を測る上で事実上の標準となっており、Web上から結果が入手しやすいから仕方ない。

  オイラが使っているコンパクトフラッシュは、ビット単価がほぼ最安で、PC用SSDとしては……

  画像の説明

  「不動の地雷評価

  ……を築いている「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面ではそこそこ十分な性能といえよう。

  次に、Write。同様に「回数」の見地から性能を測ってみる。

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時間弱もかかってしまう。

  しかも、この場合、この間ずっとReadの性能は半分になるばかりか、連続書き込みが抑制されてしまうので、フラッシュの弱点である書き込み回数が爆発的に増加してしまう。まったくの逆チューンだ。

  このヘンが判明したあたりで、いーかげん、レスポンスの悪さに嫌気が差してきたので、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

  さて、結果はというと……

  「いままでのもっさり感はなんだったんだッ!?」

  ……というくらいに、飛躍的に体感レスポンスが向上した。これは悔しまぎれでも、大げさでもない。事実、速いコンパクトフラッシュを買う必要性を感じなくなってしまった。これはスゴい。

  ただ、せっかくなので、基本的な書き出しチューンは施しておく。正直、上述の「読み書き分離」が、あまりに功を奏したので、気持ち程度であり、値は適当だ。もしかすると、デチューンしているかもしれない。

  まず、ditry(ディスクに未書き出し)ページの溜め込み割合を引き上げ、定期的な書き出しチェック頻度を下げつつ、書き出し遅延を拡大する。

# 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

  さらに、I/Oスケジューラの溜め込み数も引き上げる。これによって、連続した書き込み要求を、コマンド一発で済ませる可能性が上がるはず。

# 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

  /sysには、/procにおけるsysctl.confのような設定機構はなさそうだ。再起動時に再設定されるように、/etc/rc.localに以下を書き加えておく。

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

  なお、起動デバイスである/dev/sdaはデフォルトのままとした。もはや、このデバイスは、ほぼ読み込み専用になっており、巧みなスケジューリングは必要としないはず。

  念のため、cfq, anticipatory, deadline, noopで起動時間を計測してみたが、ログイン画面が出るまでの時間は、どれも63±1秒。回転部を持たないフラッシュメモリにとって、Readには一切の小細工は必要なさそうである。

  以上、地雷なコンパクトフラッシュでもRandom Readは、ハードディスクの10倍。ほぼ読み出し専用に使えば、書き換え寿命に対する不安も払拭である。まさに「地雷とハサミは使いよう」といえよう。

  いやぁ、回り道したものの、ひさびさに有意義な試行であった。ではまた。