ファイルサーバにする家のPC の性能を検証してみることにする。
これ以外に IDE のチップセットと カーネルのドライバも性能に影響がある。 Intel のはよくチューニングされているが、VIA の方は パッチを当てないと ちゃんとした性能にならないようだ。
Memory: 504052k/516032k available (1714k kernel code, 11596k reserved, 564k data , 252k init, 0k highmem) CPU: L1 I Cache: 64K (32 bytes/line), D cache 64K (32 bytes/line) CPU: L2 Cache: 64K (32 bytes/line) CPU: After vendor init, caps: 00803135 80803135 00000000 00000000 CPU: After generic, caps: 00803135 80803135 00000000 00000000 CPU: Common caps: 00803135 80803035 00000000 00000000 CPU0: Centaur VIA Samuel 2 stepping 03 VP_IDE: VIA vt8231 (rev 10) IDE UDMA100 controller on pci00:11.1 ide0: BM-DMA at 0xd000-0xd007, BIOS settings: hda:DMA, hdb:DMA ide1: BM-DMA at 0xd008-0xd00f, BIOS settings: hdc:pio, hdd:pio hda: ST380021A, ATA DISK drive ide0 at 0x1f0-0x1f7,0x3f6 on irq 14 hda: 156301488 sectors (80026 MB) w/2048KiB Cache, CHS=9729/255/63, UDMA(100)
何が書いてあるか、書いた本人も時間が立つと意味不明になってくるとおもうので、 まとめておく。
ext2 のファイルシステムで 1G バイトのファイルを 3 回 書いてみる。 カーネルは 改造したもの。書くコマンドは以下のもの。
time dd if=/dev/zero of=3 bs=1024k count=1000
結果は、
real 0m27.834s user 0m0.000s sys 0m17.920s real 0m29.229s user 0m0.000s sys 0m18.970s real 0m29.149s user 0m0.010s sys 0m18.690s
おおざっぱにいって 34MB/sec 程度出ている。
今度はその逆 上で作ったファイルの Read 性能をみてみる。
time dd of=/dev/null if=3 bs=1024k count=1000
結果は、
real 0m26.964s user 0m0.010s sys 0m18.840s real 0m27.501s user 0m0.000s sys 0m18.090s real 0m28.100s user 0m0.020s sys 0m18.560s
Write よりちょっと速く 35MB/sec ぐらいか。
linux カーネルの展開性能を みてやる。
mkdir $1 mkdir $1/l1 $1/l2 $1/l3 $1/l4 $1/l5 time tar -C $1/l1 -xf /root/linux-2.4.7.tar time tar -C $1/l2 -xf /root/linux-2.4.7.tar time tar -C $1/l3 -xf /root/linux-2.4.7.tar time tar -C $1/l4 -xf /root/linux-2.4.7.tar time tar -C $1/l5 -xf /root/linux-2.4.7.tar
1 つのカーネルの tar ファイル は、120MB で、 おおよそ 500 のディレクトリと 9200 個の通常ファイルを含んでいる。
これを 20 回 違うところに展開してみる。
real 0m3.725s user 0m0.680s sys 0m3.040s real 0m4.225s user 0m0.720s sys 0m3.070s real 0m4.853s user 0m0.670s sys 0m3.510s real 0m5.236s user 0m0.640s sys 0m3.420s real 0m5.391s user 0m0.580s sys 0m3.550s : : real 0m6.113s user 0m0.630s sys 0m3.590s real 0m6.222s user 0m0.690s sys 0m3.470s real 0m5.662s user 0m0.670s sys 0m3.540s real 0m6.007s user 0m0.700s sys 0m3.570s real 0m6.643s user 0m0.650s sys 0m3.780s
linux は、inode を できるだけ free しないので、 こういうケースでは段々効率が落ちていくのだが、 120MB を 6 秒ぐらいで書けるわけで、20MB/sec 程度の実力がある。 ( Disk 上のデータは、4KB 単位だから 実はこの何割か 余計に書いているし、 inode も別に書かないといけないので、実際はもっと効率が良いのだが、 とりあえず、20MB/sec としておく。 )
ext2 は 性能が出やすいわけだが、ジャーナリングファイルシステムでないと 使う気がしない。
このマシンで ext2 を使って実際に出た性能値
を、チューニングの目標とすることにする。
MB/sec の扱いについては、一応 1000 * 1024 バイト/秒 ということにする。
ここで、オペミスによって、サーバマシンの /lib を消してしまった。 ガーン。
nfs でマウントしている 内容は ちゃんと読めるようなので、 バックアップ作業をしてインストールしなおしすることになる。
バックアップ作業が最後までできれば、特にダメージはないのだが... はたして 最後までできるのか? (できなくたって特にダメージがあるわけじゃない けど ... さらに失敗を重ねるわけにはいかなくなる。)
ごみだらけなんだが、30GB も使っている。10MB/sec として バックアップに 1 時間ぐらいかかる予定。 最大の tar ファイルは、
8137523200 バイト (8 GB)しかも smb で アクセスできる領域で、漢字のファイルもあったり する。ちゃんと復元できるのか かなり不安。
インストールをしなおさないといけないのはショックだが、 reiserfs は、このマシンには向いていないようなので、構築しなおす チャンスと考えることにする。
さて、インストールのついでに vine オリジナルの 2.4.18-0vl3 の性能を試してみる。 ( ただし VIA の ide ドライバのみパッチをあてる。)
どうも、カーネルを make しただけではダメのようだ。/lib/modules/2.4.18-0vl3 がなんかおかしく 立ち上がってこない。rename して存在しないようにすれば、 立ち上がって来た。
ハード的には同じ条件での比較ということになる。
real 0m49.270s user 0m0.010s sys 0m17.320s real 0m54.139s user 0m0.000s sys 0m17.370s real 0m55.221s user 0m0.000s sys 0m17.370s
real 0m53.211s user 0m0.010s sys 0m20.350s real 0m58.179s user 0m0.010s sys 0m21.030s real 0m58.791s user 0m0.000s sys 0m20.380s
real 0m10.718s user 0m0.720s sys 0m3.720s real 0m34.504s user 0m0.730s sys 0m4.610s real 0m13.610s user 0m0.660s sys 0m3.990s real 0m24.915s user 0m0.730s sys 0m4.020s real 0m20.225s user 0m0.750s sys 0m4.080s : 20 回やるつもりだったが打ち切り。
うーん。圧倒的だなわが改造は... と思う前に、素の2.4.17 を試してみる ことにする。
記憶によると、素の2.4.17 と これほどの差はなかったはずなんだが...
real 0m40.413s user 0m0.010s sys 0m17.530s real 0m44.112s user 0m0.000s sys 0m17.610s real 0m45.288s user 0m0.020s sys 0m17.440s
real 0m35.730s user 0m0.000s sys 0m21.060s real 0m28.350s user 0m0.000s sys 0m21.710s real 0m29.984s user 0m0.000s sys 0m22.230s
real 0m10.748s user 0m0.700s sys 0m3.610s real 0m32.481s user 0m0.830s sys 0m4.180s real 0m14.059s user 0m0.750s sys 0m3.750s real 0m22.989s user 0m0.720s sys 0m4.030s real 0m20.376s user 0m0.710s sys 0m4.210s
2.4.18 は シーケンシャル I/O 性能が悪くなっているようだ。 ファイル生成の性能では、大差ないようだ。
結論は、ext2 方面では、わが改造は圧倒的だ。 ハードの性能をかなり綺麗に出して来る。
問題は、ジャーナル方面。real と sys の差を見れば... sys が 大きくなれば、real も伸びてしまうのが予想される。
で、一般に ジャーナリングファイルシステムは、ext2 と比べて sys が伸びるわけだ。
さて、上記を ext2 にして 性能を見てみる。
real 0m46.036s user 0m0.020s sys 0m26.490s real 0m52.009s user 0m0.000s sys 0m26.550s real 0m45.190s user 0m0.020s sys 0m27.630s
real 0m35.643s user 0m0.010s sys 0m21.300s real 0m35.062s user 0m0.000s sys 0m22.010s real 0m38.233s user 0m0.030s sys 0m21.750s
real 0m22.315s user 0m0.810s sys 0m5.450s real 0m22.021s user 0m0.760s sys 0m5.390s real 0m16.228s user 0m0.620s sys 0m5.330s real 0m27.939s user 0m0.720s sys 0m5.140s real 0m25.280s user 0m0.780s sys 0m5.310s
自分的改造版の real time と 素の 2.4.17 ext3 の system time の差を 見ればわかるのだが、ほとんど余裕がなくなってきている。
次に reiserfs の性能をみてみるが、system time が 大きすぎる ということになりそうだ。
real 0m55.225s user 0m0.000s sys 0m31.430s real 1m1.975s user 0m0.020s sys 0m31.480s real 0m58.197s user 0m0.010s sys 0m31.430s
real 0m46.387s user 0m0.000s sys 0m23.540s real 0m42.069s user 0m0.010s sys 0m23.150s real 0m42.040s user 0m0.010s sys 0m23.200s
real 0m15.090s user 0m0.980s sys 0m10.040s real 0m21.092s user 0m0.710s sys 0m10.740s real 0m32.868s user 0m0.740s sys 0m11.700s real 0m16.919s user 0m0.820s sys 0m12.360s real 0m25.489s user 0m0.880s sys 0m11.550s
シーケンシャル I/O 特に Write の場合、DISK の性能より CPU の使用の 方が大きい。
tar を使った ファイル生成の場合 はもっと顕著になってきそうだ。
高度な DISK I/O の最適化をしても 家のPC --- C3 533Mhz 程度では CPU ネックになってしまって、改造しても メリットがないということに なる。
というわけで、CPU がおそいマシンでは、ext3 の方が良いという結論。
さて、ext2 方面では絶大な効果があったわが改造だが、ext3 の場合はどうなのか。
実は、write_some_buffers ではないパスで書き出されているらしく、 効果がないようなのだ。
これを確かめてみる。
real 0m47.080s user 0m0.000s sys 0m26.670s real 0m48.385s user 0m0.000s sys 0m27.310s real 0m46.823s user 0m0.000s sys 0m26.820s
real 0m37.829s user 0m0.020s sys 0m20.860s real 0m35.224s user 0m0.000s sys 0m21.890s real 0m33.116s user 0m0.010s sys 0m21.290s
real 0m24.988s user 0m0.740s sys 0m5.760s real 0m17.723s user 0m0.770s sys 0m5.430s real 0m21.136s user 0m0.770s sys 0m5.510s real 0m24.289s user 0m0.720s sys 0m5.520s
かなしいかな、圧倒的性能はどこかに行ってしまった。
シーケンシャル Readまで、おそくなってしまったが、これはどうしてか。 予想は、mm/vmscan.c の shrink_cache で ダーティなページが沢山あると その処理に引きずられて、待ちが発生してしまうというものだったのだが、 どうも違うようだ。
一旦 umount して 書き残しがないことを保証して 再度 mount するとどうなるかというと
real 0m39.146s user 0m0.000s sys 0m21.560s real 0m34.491s user 0m0.010s sys 0m21.830s real 0m34.799s user 0m0.000s sys 0m21.350s
うーん。あんまりかわらん。
real 0m28.227s user 0m0.010s sys 0m19.900s real 0m31.197s user 0m0.040s sys 0m20.690s real 0m30.315s user 0m0.010s sys 0m20.840s 最後(3回目)の IDE 割り込み回数 8164 回
real 0m39.899s user 0m0.000s sys 0m21.720s real 0m32.075s user 0m0.010s sys 0m21.290s real 0m35.190s user 0m0.010s sys 0m21.300s 最後(3回目)の IDE 割り込み回数 15893 回
かならずしも、Read 性能は安定しないようだ。基本的に DISK にどのように データが割り付けられるかによって、Read 性能は変わって来る。
綺麗に割り付けられないと Read 性能はでない。
read-ahead とか pre-read と呼ばれる先読み機構が役立つか どうか ... というのも関係してくる。Linux のコードをみると 先読みのデータは、メモリに残らない。うまくシステムの buffer に 先読みのデータを取りこめれば、すこしは性能が安定することが期待できそうだ が、無駄な I/O にならないようにバランスを取るのが難しそうだ。
ちなみに Write の場合、ダーティな領域が連続してさえいれば良い ようにしたので、必ずしも綺麗に割り付けられなくとも ある程度性能が 出せてしまう。
シーケンシャル I/O 性能は とりあえずおいておいて、 まずは、ext3 を使ったときの tar での展開性能のみ チューニングしてみることにしよう。
SGI では、linux 用のカーネルプロファイラを公開している。
とりあえず、2.4.17 で PC サンプリングでの データを採取してみて どうCPU が使われているか あたりをつけてみよう。
これで良いかどうかわからないが、いつも 次のようにしている。
kernprof -r kernprof -b -t pc 測定したい処理 kernprof -e kernprof -i -m /boot/System.map > p002.out
サンプリングする期間は長ければ長いほど良いのだが、とりあえず カーネル tar 展開 10 回分。
TOTAL_SAMPLES 5518 ( - idle : 50.62 秒) file_read_actor [c012a8cc]: 1036 generic_file_write [c012bf5c]: 858 USER [c0126680]: 676 __block_prepare_write [c01394e4]: 563 default_idle [c01053e0]: 456 __get_lru_hash_table [c01383cc]: 105 ext2_new_block [c01689dc]: 99 prune_icache [c014a434]: 86 __make_request [c01ee20c]: 78 link_path_walk [c014004c]: 74 ide_end_request [c020c0bc]: 49 d_lookup [c0148d10]: 43 rmqueue [c0130b58]: 41 ext2_find_entry [c016995c]: 41 ext2_add_link [c0169b9c]: 37 system_call [c0107050]: 34 write_locked_buffers [c013791c]: 31 __remove_from_lru_list [c0138234]: 31 mark_buffer_dirty [c0138ddc]: 30 do_generic_file_read [c012a2f0]: 29 kmem_cache_alloc [c012ed98]: 27 shrink_cache [c012ff8c]: 26
__get_lru_hash_table というのは、改造したところで、隣り合った block がダーティかどうか調べるところ。
prune_icache というのは、inode を shrink しようとするところで、 inode のリストを 検索している。
ほぼすべての関数は、処理内容を知っているか想像できる。
TOTAL_SAMPLES 21818 ( - idle : 68.36秒) default_idle [c01053e0]: 14982 file_read_actor [c012a8cc]: 958 generic_file_write [c012bf5c]: 827 USER [c0126680]: 721 __block_prepare_write [c01394e4]: 486 journal_get_write_access [c0162dbc]: 326 ext3_do_update_inode [c015cda8]: 256 journal_dirty_metadata [c0163284]: 236 do_get_write_access [c01628d0]: 146 __make_request [c01ee20c]: 129 get_hash_table [c013832c]: 103 journal_cancel_revoke [c0166564]: 96 ext3_get_inode_loc [c015c984]: 77 ext3_add_entry [c015dfb0]: 73 link_path_walk [c014004c]: 72 start_this_handle [c0162228]: 64 journal_add_journal_head [c0168120]: 60 journal_unlock_journal_head [c016834c]: 57 ext3_check_dir_entry [c0158cb0]: 56 ext3_new_block [c0158454]: 50 kmem_cache_alloc [c012ed98]: 49 journal_commit_transaction [c01640d8]: 48 journal_stop [c01635b4]: 45 find_next_usable_block [c0158194]: 44 do_rw_disk [c0217c98]: 43 d_lookup [c0148d10]: 43 ext3_find_entry [c015dc70]: 41 rmqueue [c0130b58]: 40 __insert_into_lru_list [c0138158]: 39 __remove_from_lru_list [c0138234]: 37 __journal_file_buffer [c0163de0]: 32 do_generic_file_read [c012a2f0]: 31 ext3_get_block_handle [c015ae2c]: 30 strncpy_from_user [c029e2b8]: 29 kmem_cache_free [c012f00c]: 29 kmalloc [c012eeac]: 29 journal_start [c0162390]: 29 ide_dmaproc [c02128cc]: 29 system_call [c0107050]: 27 ext3_new_inode [c01598dc]: 26 __wake_up [c0116a88]: 25 ext3_commit_write [c015b7c4]: 25 ide_end_request [c020c0bc]: 24 __brelse [c0138eac]: 24 shrink_cache [c012ff8c]: 23 mark_page_accessed [c012a2ac]: 23 ext3_dirty_inode [c015d5b8]: 23 journal_get_undo_access [c0162fc8]: 22 __mark_inode_dirty [c01496a8]: 21 __free_pages_ok [c0130960]: 21 ext3_mark_inode_dirty [c015d584]: 21 ide_build_sglist [c0212328]: 20 ext3_prepare_write [c015b4ec]: 20 bread [c0138f00]: 20 __block_commit_write [c01396dc]: 20 ext3_alloc_branch [c015a9f4]: 19 vfs_permission [c013fba0]: 18 path_init [c01409b8]: 18 ext3_mark_iloc_dirty [c015d48c]: 18 refile_buffer [c0138e88]: 17 kfree [c012f088]: 17 __journal_remove_journal_head [c0168220]: 17 ext3_test_allocatable [c015814c]: 17 ext3_getblk [c015b13c]: 17 create_buffers [c01390b8]: 17 ll_rw_block [c01ee974]: 16 handle_IRQ_event [c01087ac]: 16
はっきりいってよくわからん。 ざっと見て知っている関数の割合が少ないからどうやって動いているか 想像が働かない。
なさけないことに、それぐらいしかわからん。 ううむどうやって調べようか。
基本的に CPU の 効率をあげるのが目的でなくて、I/O 待ちが発生する ケースを極力へらしたいわけだ。
とりあえずは、kdb を入れて、測定の途中で 止め、どこで待ちが 発生するか見てやることにしよう。
メモ: カーネルプロファイラ の結果をみて気がついたこと
: ext3_create ext3_new_inode ext3_mark_inode_dirty ext3_reserve_inode_write ext3_get_inode_loc bread __wait_on_buffer schedule
tar のプロセスが、こういうところでいつも待ちに入っている。
ログを取るために、シリアルコンソールが欲しいところ。
また、デフォルトだと画面がスクロールして、全部が見えない。
ここは、kdb/kdbmain.c の LINES を18 ぐらいにすれば、とりあえず OK。
ふうむ。create するたびに、I/O 待ちをすることになってしまっている わけだ。
/* * ext3_get_inode_loc returns with an extra refcount against the * inode's underlying buffer_head on success. */ int ext3_get_inode_loc (struct inode *inode, struct ext3_iloc *iloc) { : offset = ((inode->i_ino - 1) % EXT3_INODES_PER_GROUP(inode->i_sb)) * EXT3_INODE_SIZE(inode->i_sb); block = le32_to_cpu(gdp[desc].bg_inode_table) + (offset >> EXT3_BLOCK_SIZE_BITS(inode->i_sb)); if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
__wait_on_buffer で待ちに入るというのは、どういうことだろう。
本当に read しているのか、それとも write 中なので、write が終るまで
待つということなのか? とにかく I/O 中だから buffer_locked(bh) でない間
schedule() で待つわけだ。
後者だとすれば I/O 効率が既に悪いというのが原因の可能性が高くなってくる。
もしそうだとすれば....ここは単なる被害者である。inode block を
書き出しているところが悪いかといえば、そうも言えない。
ばらばらに I/O が出ているのが
そもそもの原因であって、書き出しているところすら被害者という可能性がある。
うーん。だんだん面倒になってきた。
とりあえず、__wait_on_buffer の対象になっている buffer_head を見てみる。
なるほど、write_some_buffers の延長の write_locked_buffers で書かれる 場合は、__end_buffer_io_sync にしたから、それ以外で書かれたという ことか。それに該当するのは、sync_page_buffers ... ではなかった。 bp で break point をかけたがそこでは止まらない。 それ以外では、drivers/block/ll_rw_block.c の ll_rw_block 以外考えられない。
kdb で止める。ll_rw_block に bp する。break point で stack trace をして bc でクリアするというのをくり返すと。
という2つがひっかかった。後者は、
log_wait_for_space log_do_checkpoint __flush_buffer __flush_batch ll_rw_block
というながれ、もしこれだとすれば、log の space がないってことだ。
ちょっとチェックしてみる。
mkfs.ext2 -j だと。 Creating journal (8192 blocks): done
となった。
mkfs.ext2 -j -J size=128 /dev/hda3 なら、 Creating journal (32768 blocks): done
デフォルト real 0m24.988s user 0m0.740s sys 0m5.760s real 0m17.723s user 0m0.770s sys 0m5.430s real 0m21.136s user 0m0.770s sys 0m5.510s real 0m24.289s user 0m0.720s sys 0m5.520s journal 拡大 real 0m13.448s user 0m0.760s sys 0m5.380s real 0m19.587s user 0m0.740s sys 0m5.410s real 0m22.900s user 0m0.600s sys 0m5.560s real 0m23.106s user 0m0.660s sys 0m5.480s
うーん。あんまりかわりばえしないなぁ。
あ、b_state に、BH_Uptodate が入っていないことを見落していた。 ということは、純粋に read しているってことか。
となると.... inode のまわりも 先読みしておいて、buffer に載せておく ってのが、正しい方法?
とりあえず、上記の ext3_get_inode_loc 専用の bread_zone というのを 作ってみた。
という改造。前にもしたことがあるが、再度挑戦。 今度は ワンポイントで使う。
結果は、
real 0m6.968s user 0m0.570s sys 0m5.700s real 0m6.860s user 0m0.690s sys 0m5.780s real 0m22.658s user 0m0.560s sys 0m5.860s real 0m8.205s user 0m0.850s sys 0m5.460s real 0m9.805s user 0m0.760s sys 0m5.660s real 0m24.138s user 0m0.880s sys 0m5.470s real 0m20.787s user 0m0.740s sys 0m5.730s real 0m15.381s user 0m0.670s sys 0m5.820s real 0m21.191s user 0m0.790s sys 0m5.600s
ダーティなバッファがスムーズに書き出されれば、6-7 秒台を維持できそう な気がしてきた。
さて、ちょっと 止めてみたところ write_full_page の延長で、
journal_dirty_sync_data journal_dirty_data ll_rw_block wait_on_buffer
というパスで write しているのが見付かった。
どうもこれは、
if (ext3_should_order_data(inode)) { ret = walk_page_buffers(handle, page->buffers, from, to, NULL, journal_dirty_sync_data); }
で動くところのようだ。 walk_page_buffers の意味は、foreach 程度に考えれば良いようなので、 ext3_should_order_data() あたりが本質的な意味を持っているように 思える。
/* * journal_dirty_data: mark a buffer as containing dirty data which * needs to be flushed before we can commit the current transaction. : int journal_dirty_data (handle_t *handle, struct buffer_head *bh, int async) {
うーん。なんで、current transaction. するまでに、flush しておかないと いけないのか? ジャーナリングファイルシステムでは、 書き順があるのは確かなのだが、
なるほど、ディレクトリデータのことなのか。
if (!S_ISREG(inode->i_mode)) return 1;
これだけはさすがに ... とは思うが、xfs とかは、そういう処理だったかなぁ。
さて、何度か止めてみてみると
write_some_buffers write_locked_buffers submit_bh generic_make_request: __make_request __get_request_wait schedule
で 待ちになっているケースが目立つ。
if (q->rq[rw].count < batch_requests)
ということらしいんだが...
batch_requests はなんと 32 ... エ?
drivers/block/ll_rw_block.c queue_nr_requests = 64; if (total_ram > MB(32)) queue_nr_requests = 128; batch_requests = queue_nr_requests/4;
どうもこれがひっかかっているようだ。
とりあえず 4 倍にしてみる。
シーケンシャル Write でどこで止まっているのか?
kdb で止めつつ見ていると、__get_request_wait でやっぱり止まっているのと
read_block_bitmap の bread で止まっているのが みつかった。
この2つをやってみるとなんとか 40 秒を切った。
real 0m37.398s user 0m0.010s sys 0m28.250s real 0m38.809s user 0m0.010s sys 0m29.250s real 0m35.704s user 0m0.000s sys 0m29.090s 14: 49339 XT-PIC ide0 14: 57752 XT-PIC ide0
統計情報 (/proc/stat ) を見てみる。
disk_io: (3,0):(170765,42488,1199584,128277,23257056) : fstune 6161 197136 8732 254299 0 128147 34292 3920 0 0 0 0 0 0 0 0
一番下は、今回の改造で追加しているもの。
どう読むかというと、立ち上げから Disk に対し 42488 回の read と 128277 回の write が起きている。
Disk に対する block 数 (512B 単位)は、 read 1199584 blk / write 23257056 blk 。
fstune の方は、write_some_buffers で書いた 4k block は、 197136 blk および 254299 blk。
write 量で比較すれば、
write_some_buffers を通して書いたものは、全体の 15.5 % にしか過ぎない。
ただし、DISK への 平均 write サイズは、90 KB でそんなには小さくない。 write_some_buffers をみると、2 種類の書きかたがあって、4K / 116.49 KB 。
うーん。write_some_buffers 以外で書く量が多すぎるから効果がないわけか。
シーケンシャル Write
Disk Write 量 2884.804 MB Disk Write 平均サイズ 123 KB write_some_buffers 通過量 2885.912 MB write_some_buffers 平均サイズ 1869 KB
ところが、おなじ ようにしても ext3 ではこんなに性能がでない。
Disk Write 量 3010.188 MB Disk Write 平均サイズ 116 KB write_some_buffers 通過量 35.264 MB journal_commit_transaction 通過量 2972.048 MB
ふうむ。journal_commit_transaction での ll_rw_block がメインな わけか。
では tar で展開したときはどうなんだろう?
Disk Write 量 1376.748 MB Disk Write 平均サイズ 59 KB write_some_buffers 通過量 40.932 MB journal_commit_transaction 通過量 1277.532 MB
なるほど、ようやくわかった。journal_commit_transaction のしくみが どうなっているか 見て行けば いいわけだ。
ちょっとみてみると
jh = commit_transaction->t_sync_datalist next_jh = jh->next_jh という感じで、journal_head がリストを形成している。 それぞれの jh に対して jh->b_bh で buffer_head がリンクしていて 最大 64 個の 配列に詰めて 一気に ll_rw_block で書き出す。 それを空になるまで、くり返す。
となると、1 つの commit_transaction にいったい いくつの buffer_head がくっついているか。が重要だ。
シーケンシャル Write の場合 37 回の journal_commit_transaction で、 11471 ページを 書き出していた。平均 310.027 ページ。 時間は取っていないのでわからないんだけども だいたい 120 秒。
tar の展開では、63 回の journal_commit_transaction で、 5118 ページを 書き出していた。平均 81.238 ページ。 時間は取っていないのでわからないんだけども だいたい 180 秒。
journal->j_commit_interval = (HZ * 5);
なんてコードがあるけども、フル性能で動いたときも 3 秒間隔ぐらいで journal_commit_transaction されるようだ。
で、ll_rw_block で リクエストキューにつなげてしまってエレベータシーク に頼ると、 59 KB 程度の I/O サイズにしかならない。 シーケンシャル Write の 116 KB でも不充分で Disk の性能を発揮する ところまではいかない。
正確かどうかはあやしいが、まぁだいたいそんな風に考えてよさそうだ。
ext2 で非常にうまくいくのは、123 KB での I/O サイズ を越えて 論理的には、1869 KB まで つながっているからだと考えている。
これをなんとかするには、ll_rw_block を直接呼ばないで、 一旦 バッファリングする。そこで、write_some_buffers でやっているような 手法で、I/O をまとめていく。 そういったことをすれば良いのだろう。
ただし、これをするのはとても面倒だし、はたして、効果がどれぐらい あるか...
もうすこし楽なやりかたでやってみたほうが良いかも。
fstune-2.4.17-001 の性能をみてみる。
real 0m35.958s user 0m0.000s sys 0m34.620s real 0m37.771s user 0m0.000s sys 0m35.190s real 0m37.642s user 0m0.030s sys 0m35.140s
real 0m42.988s user 0m0.010s sys 0m22.870s real 0m42.237s user 0m0.020s sys 0m23.100s real 0m43.096s user 0m0.010s sys 0m23.540s
real 0m11.790s user 0m0.820s sys 0m10.480s real 0m13.756s user 0m0.780s sys 0m11.940s real 0m13.906s user 0m0.950s sys 0m11.820s real 0m13.549s user 0m0.840s sys 0m11.670s real 0m14.839s user 0m0.770s sys 0m12.790s
決して おそいわけではないし、ext2 のようにチューニングが効く んだが... いかんせんうちの CPU はおそい。
プロファイラで見てみると、特定の関数が CPUを使っているのは 明らかなので、改善されてくるかも知れない。
圧倒的性能は喪われていないか。
real 0m26.237s user 0m0.010s sys 0m19.400s real 0m28.222s user 0m0.000s sys 0m20.590s real 0m31.101s user 0m0.010s sys 0m20.790s
real 0m38.063s user 0m0.010s sys 0m20.890s real 0m34.732s user 0m0.000s sys 0m21.220s real 0m33.417s user 0m0.000s sys 0m21.330s
real 0m4.200s user 0m0.700s sys 0m3.280s real 0m4.574s user 0m0.720s sys 0m3.670s real 0m4.796s user 0m0.660s sys 0m3.940s real 0m5.164s user 0m0.680s sys 0m3.940s real 0m5.247s user 0m0.660s sys 0m3.980s
なんか Write も性能がばらつくし、Read は、20 秒台が出なくなってしまった。