はじめに
先の記事(ext4のnoauto_da_allocオプションの詳細)でnoauto_da_allocオプションがどう効くかをコードベースで確認したが、これが実際にどれくらいパフォーマンスに響くのかを簡易にテストしてみた。
テスト環境は、Ubuntu 14.04(Linux-3.13くらい)、i7-4790Kくらい、HDDはよくあるSATA接続のもの、RAMはたくさんめです。
テストスクリプト
#!/bin/sh
dd if=/dev/zero of=test.dat bs=64k count=1024
dd if=/dev/zero of=test.dat bs=64k count=1024
dd if=/dev/zero of=test.dat bs=64k count=1024
dd if=/dev/zero of=test.dat bs=64k count=1024
dd if=/dev/zero of=test.dat bs=64k count=1024
dd if=/dev/zero of=test.dat bs=64k count=1024
dd if=/dev/zero of=test.dat bs=64k count=1024
dd if=/dev/zero of=test.dat bs=64k count=1024
dd if=/dev/zero of=test.dat bs=64k count=1024
dd if=/dev/zero of=test.dat bs=64k count=1024
#!/bin/sh
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
dd if=/dev/zero of=test.dat bs=64k count=1024
rm test.dat
テスト結果
noauto_da_allocなしの時(デフォルト)
[rarul@tina ~]$ time ./ddtest_pc1.sh 2&>1 > /dev/null
real 0m6.020s
user 0m0.000s
sys 0m0.447s
[rarul@tina ~]$ time ./ddtest_pc1.sh 2&>1 > /dev/null
real 0m5.888s
user 0m0.000s
sys 0m0.376
[rarul@tina ~]$ time ./ddtest_pc2.sh 2&>1 > /dev/null
real 0m0.384s
user 0m0.005s
sys 0m0.379s
[rarul@tina ~]$ time ./ddtest_pc2.sh 2&>1 > /dev/null
real 0m0.319s
user 0m0.007s
sys 0m0.312s
noauto_da_allocありの時(オプション変更)
noauto_da_allocでremountする。
# mount -o remount,noauto_da_alloc / /
でテストを開始、
# mount -o remount,noauto_da_alloc / /
[rarul@tina ~]$ time ./ddtest_pc1.sh 2&>1 > /dev/null
real 0m0.385s
user 0m0.005s
sys 0m0.379s
[rarul@tina ~]$ time ./ddtest_pc1.sh 2&>1 > /dev/null
real 0m0.305s
user 0m0.004s
sys 0m0.301
ついでにnodelallocもテストする
fstabでnodelallocを足して再起動してテスト、時間置いてからの一発目がなぜか遅いという症状が出るものの、おおむね想定通りの速度。
[rarul@tina ~]$ time ./ddtest_pc1.sh 2&>1 > /dev/null
real 0m1.199s
user 0m0.000s
sys 0m0.441s
[rarul@tina ~]$ time ./ddtest_pc1.sh 2&>1 > /dev/null
real 0m0.436s
user 0m0.000s
sys 0m0.436s
[rarul@tina ~]$ time ./ddtest_pc2.sh 2&>1 > /dev/null
real 0m0.473s
user 0m0.004s
sys 0m0.442s
[rarul@tina ~]$ time ./ddtest_pc2.sh 2&>1 > /dev/null
real 0m0.443s
user 0m0.003s
sys 0m0.440s
ただ、remountではなぜかnodelallocがうまく効いていないようなベンチマーク結果だった。/proc/mounts的にはnodelallocになってたんだけどなぁ。ext4のコードを再確認して見るもそれらしきは見あたらずに原因はよくわからない。vopsまで含めてinodeがキャッシュされていたということにしておこう...
# mount -t ext4 -o remount,nodelalloc / /
テスト結果の解説
ext4はデフォルトでは、
- renameで上書きが発生するタイミング
- truncateでサイズ0にした場合のcloseのタイミング
の2つの場合に、filemap_flush()を呼ぶことでIOをスタートさせようとする。通常はバッファキャッシュでIOが遅延され、場合によってはIOがスタートする目に消された結果何もしないのが、filemap_flush()が呼ばれることで必ずIOがこのタイミングでスタートしてしまう。noauto_da_allocをつけるとこの挙動を行わなくなるのでこの特定ケースでパフォーマンスが向上する。
前者はともかく、後者はユーザランドレベルではもっと具体的に下記の場合になるかと思う。
- ファイルが存在する場合にopen(2)をO_TRUNCで呼ぶ
- truncate(2)でファイルサイズをゼロにする
「ファイルを消して作り直すくらいならO_TRUNCでファイルサイズをゼロにしてしまう」なんていうのはプログラムによってはやってそうな処理ではある。SQLiteをjournal_mode=truncateで使っているとか。
終わりに
先にソースコードを読んだ上での恣意的なテストだったというわけで、なんという出来レース...