この記事は TECHSCORE Advent Calendar 2016 の 4 日目の記事です。
I. はじめに
システムの運用・構築・実装などで、ディスクへの書き込みバッファを4KB/8KBにすることって多いですよね? 例えば弊社でよく利用しているDBMSである 「PostgreSQL」は、ディスクへの書き込みは8KB単位で行われますし、手元のJavaでOutputStreamWriterを確認してみると8KBでバッファリングされているようでした。
なぜこのサイズなのでしょう?? 気になりますね。適当に大きめのサイズ、3KBとか5KBではダメなんでしょうか?
ディスクへのr/w要求はファイルシステムのブロックサイズ単位で行われるので、ブロックサイズとからめつつ検証してみます!
II. テストプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import java.io.RandomAccessFile; public class WriteBench { public static void main(String[] args) throws Exception { String fileName = "/mnt/test/test.dat"; int fileSize = 1024 * 1024 * 128; int dataSize = 2048; WriteBench writeBench = new WriteBench(); long execMillis = writeBench.bench(fileName, fileSize, dataSize); System.out.println(execMillis + "msec"); } private long bench(String fileName, int fileSize, int dataSize) throws Exception { byte[] writeData = new byte[dataSize]; long start = System.currentTimeMillis(); try (RandomAccessFile f = new RandomAccessFile(fileName, "rw")) { for (int i = 0; i < fileSize / dataSize; i++) { f.write(writeData); } } return System.currentTimeMillis() - start; } } |
単純に/mnt/test/test.datに128MBをシーケンシャルライトするプログラムです。dataSizeを2048, 4096, 6144, 8192と変更しながら書き込みにかかった時間を計測してみます。
III. ext4 ブロックサイズ4KBでの検証
mkfs.ext4 –b 4096 でフォーマットして検証してみます。
A. 追記でのパフォーマンス
テストプログラム実行前に、毎回下記コマンドを実行して、新規ファイル(追記)としてテストします。
1 |
$ rm /mnt/test/test.dat |
dataSizeを2048, 4096, 6144, 8192としたそれぞれの実行結果です。
(結果はそれぞれ5回実行して平均をとっています)
4096 -> 104 msec
6144 -> 105 msec
8192 -> 83 msec
バッファサイズが大きい方が若干速いですね。
B. 上書きでのパフォーマンス
次は上書きのテストです。上書きなのでファイルは削除せず、ページキャッシュだけ消します。
1 2 |
$ sync $ echo 1 > /proc/sys/vm/drop_caches |
dataSizeを2048, 4096, 6144, 8192としたそれぞれの実行結果です。
4096 -> 69 msec
6144 -> 1646 msec
8192 -> 59 msec
これは面白い結果になりました。バッファサイズ2048,6144の結果が極端に悪いです。
C. 上書きでのパフォーマンス(ページキャッシュあり)
ページキャッシュにファイルの内容がのっている状態での上書きはどうなるか? テストしているマシンはメモリに余裕があるので、128MBほどであればすべてキャッシュにのります。
1 2 3 4 |
$ free -m total used free shared buff/cache available Mem: 5685 1327 4137 8 219 4281 Swap: 1023 0 1023 |
dataSizeを2048, 4096, 6144, 8192としたそれぞれの実行結果です。
4096 -> 53 msec
6144 -> 51 msec
8192 -> 41 msec
速いですね! 全体的に追記の場合と傾向が似ていて、若干追記よりも速いです。
IV. ext4 ブロックサイズ2KBでの検証
では次に、ファイルシステムのブロックサイズを2KBに変更して同じテストを行ってみましょう。
mkfs.ext4 –b 2048 でフォーマットして検証してみます。
A. 追記でのパフォーマンス
先ほどと同じように毎回テストファイル削除して実行していきます。
1 |
$ rm /mnt/test/test.dat |
dataSizeを2048, 4096, 6144, 8192としたそれぞれの実行結果です。
4096 -> 117 msec
6144 -> 102 msec
8192 -> 93 msec
mkfs.ext4 –b 4096 とあまり結果が変わらないですね。ファイルシステムのブロックサイズはあまり関係ないのでしょうか??
B. 上書きでのパフォーマンス
上書きのテストはどうなるか? ページキャッシュなしでテストします。
1 2 |
$ sync $ echo 1 > /proc/sys/vm/drop_caches |
dataSizeを2048, 4096, 6144, 8192としたそれぞれの実行結果です。
4096 -> 75 msec
6144 -> 66 msec
8192 -> 55 msec
これも面白い結果になりました。バッファサイズ2048, 6144の結果がmkfs.ext4 –b 4096とは段違いに速いです!
C. 上書きでのパフォーマンス(ページキャッシュあり)
ページキャッシュありの場合もテストします。
dataSizeを2048, 4096, 6144, 8192としたそれぞれの実行結果です。
4096 -> 75 msec
6144 -> 66 msec
8192 -> 57 msec
mkfs.ext4 –b 4096の場合と似た傾向ですね。若干こちらの方が遅いです。
V. 考察してみる
LinuxでのI/O処理は、ページキャッシュが存在するかしないかが大きく関わります。通常の書き込み処理は、常にページキャッシュに対して行われます。また、ファイルシステムはブロックサイズ単位でI/O処理を行います。
上書き処理がどのように行われるか、考えてみましょう。
上書き処理はデータの変更処理ですから、ページキャッシュに変更箇所のブロックが読み込まれていない場合、まず先にディスクから読み込まなければなりません。読み込み後、ページキャッシュ上で変更を書き込みます。なので、ページキャッシュのアリ・ナシで劇的にパフォーマンスが変わります。
■例1 4KBブロックのファイルシステムで2KB上書きを行う場合の処理イメージ
しかし、これには例外があります。変更箇所のブロックすべてを書き換える場合、データの読み込みが必要ないため、ページキャッシュ内で処理が完結します。
■例2 4KBブロックのファイルシステムで4KB上書きを行う場合の処理イメージ
検証では、4KBブロックサイズでの2KB, 6KBの書き込み処理が際立って遅かったですね。これは例1のケースがあてはまります。書き込み処理のつもりが、実は読み込み処理が先に実行されているという状況です。読み込みの間、プロセスは待たされ、I/O Waitに計上されます。
読み込みが頻発する様子は、テストプログラムを実行中にblktraceコマンドを使用するとよくわかります。
■4KBブロックのファイルシステムで2KB書き込みテスト中のI/Oリクエスト
1 |
$ blktrace –d /dev/sdb1 –o - | blkparse – |
ブロック全体を書き換えるような書き込み、つまり、ファイルシステムブロックサイズの整数倍の書き込みは速いのです。2KBブロックサイズの検証結果が安定していたのはそのためですね。書き込みサイズ1KBでテストすると、2KBブロックサイズの場合も遅くなります。
では追記の場合はどうでしょうか? ブロックサイズ4KB, 2KBともに安定していました。
ファイルの追記は、ファイルの末尾以降の、論理的にデータが存在しない領域への書き込みです。ファイルシステムが新規獲得したブロックのページキャッシュに書き込むだけで完了となるため、基本、追記は常に安定して速いものです。ファイル末尾がブロックサイズ未満だった場合は読み込みが発生します。
少し話が長くなってしまいました。検証結果からもわかりますが、ページキャッシュにさえのっていれば、あまり深く考えずとも常に安定して速いですね。サーバのサイジングでは、稼働させるソフトウェアのI/O特性を把握したうえで、ページキャッシュが力を発揮できるよう搭載メモリを決定する必要がありますね。
さて、本題の書き込みバッファ4KB, 8KBってどうなの?? ですが、4KB, 8KBでファイルシステムブロックサイズの整数倍とならないケースはまずない。と思いますので、妥当そうです。将来、もっと大きなブロックサイズが一般化してきた場合には検討する必要がありそうですね。
ではよいクリスマスを! (ツ)/