[Linux] ディスク性能テストにfioを使ってみる | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

アプリケーション規模が大きくなってくるとボトルネックの1つとしてディスクの性能とかが気になってきたりして、どのくらいのパフォーマンスが出せるものかテストをしたくなってきたりもします。

で、調べてみるとfioというコマンドがあってFlexible I/O Testerの略らしいんですけど、テスト対象のディスク性能を数値化してくれたりします。

ここでの実行環境はRedHat6をベースにしてます。

 

 

fioの使い方

 

各ディストリビューションのパッケージとしても用意されているようですが、ここではソースからのインストール方法を。

ソースは、GitHub上から適当なバージョンをダウンロードします。

後はお決まりのconfigureからのmake installとなるのですが、その前にfioのディスク書き込みテストを非同期で行いたい場合はlibaioというライブラリが必要になってきます。

そこで、このパッケージを事前にインストールしておきます。

 

# yum install libaio-devel.x86_64

 

インストール完了後はfioのインストール。

 

$ ./configure

$ make

# make install

 

fioコマンドの使い方は非常にオプションが多いのですが、

 

# fio --ioengine=libaio --iodepth=16 --direct=1 --size=100m --numjobs=16 --group_reporting --directory=/tmp --bs=4k --output=/root/result.txt --name=random-r70w30 --rw=randrw --rwmixread=70

 

のように使います。 ioengineオプションはlibaioで先ほどのライブラリを指定していますが、IOをどういう風に発生させるかを決めるおオプションです。

同期書き込み(デフォルトのsync)や非同期のlibaioなどを指定します。 使用できるioengineは下記のヘルプで確認できます。

 

# fio --enghelp                                                                                                                                                         
Available IO engines:
        binject
        sg
        splice
        e4defrag
        falloc
        posixaio
        libaio
        filecreate
        ftruncate
        net
        netsplice
        null
        sync
        psync
        vsync
        pvsync
        pvsync2
        mmap
        cpuio

 

次に、iodepthは同時書き込みをどれくらいの数で発生させるかというオプションで、numjobsも同じ数にしていますがこちらも同時実行スレッド数を指定するものなのですがあんまり違いが判ってません・・・。

sizeはテスト用ファイルのサイズ、directoryは書き込み先のディレクトリ、bsはブロックサイズです。

rwオプションはテストの読み書き方法でreadを指定すれば読み込みのみを、randwriteを指定すればランダムの書き込みを、上記のよう見randrwを指定すればランダムな読み書きを実行します。

併せてrwmixreadオプションは読み書きを実行する場合にどれくらいの割合にするかを指定するもので、この場合は70%がread(つまり読み込み)、残りが書き込みテストとなります。

書き込みを多くテストしたい場合はこの割合を調整すればよいです(逆の意味を指定するrwmixwriteというオプションもありますが)。 

これ以外にもオプションが豊富なので、その他のオプションを確認したい場合は、他の解説しているサイトなどをご参照ください・・・。 

 

fioコマンド @ 新宿区で働くプロジェクトマネージャーのブログ

 

で、実行結果。

 

random-r70w30: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=16
...
fio-3.2
Starting 16 processes
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)

random-r70w30: (groupid=0, jobs=16): err= 0: pid=2394: Sat Dec 19 22:17:11 2017
   read: IOPS=2148, BW=8593KiB/s (8799kB/s)(1119MiB/133291msec)
    slat (usec): min=4, max=274614, avg=3149.74, stdev=13009.06
    clat (usec): min=122, max=643285, avg=82131.10, stdev=32362.04
     lat (usec): min=405, max=643293, avg=85282.41, stdev=34297.45
    clat percentiles (msec):
     |  1.00th=[   17],  5.00th=[   34], 10.00th=[   50], 20.00th=[   62],
     | 30.00th=[   69], 40.00th=[   75], 50.00th=[   81], 60.00th=[   86],
     | 70.00th=[   91], 80.00th=[   99], 90.00th=[  113], 95.00th=[  138],
     | 99.00th=[  199], 99.50th=[  224], 99.90th=[  288], 99.95th=[  351],
     | 99.99th=[  464]
   bw (  KiB/s): min=   56, max= 1898, per=6.27%, avg=538.82, stdev=140.85, samples=4242
   iops        : min=   14, max=  474, avg=134.40, stdev=35.18, samples=4242
  write: IOPS=924, BW=3699KiB/s (3788kB/s)(481MiB/133291msec)
    slat (usec): min=6, max=242011, avg=3140.93, stdev=12863.42
    clat (usec): min=182, max=643278, avg=74573.84, stdev=32149.02
     lat (usec): min=260, max=643287, avg=77716.39, stdev=31591.33
    clat percentiles (msec):
     |  1.00th=[   15],  5.00th=[   23], 10.00th=[   32], 20.00th=[   52],
     | 30.00th=[   63], 40.00th=[   70], 50.00th=[   77], 60.00th=[   82],
     | 70.00th=[   87], 80.00th=[   94], 90.00th=[  106], 95.00th=[  124],
     | 99.00th=[  176], 99.50th=[  207], 99.90th=[  249], 99.95th=[  296],
     | 99.99th=[  502]
   bw (  KiB/s): min=   32, max=  844, per=6.27%, avg=231.76, stdev=68.16, samples=4242
   iops        : min=    8, max=  211, avg=57.82, stdev=17.00, samples=4242
  lat (usec)   : 250=0.01%, 500=0.01%, 750=0.02%, 1000=0.01%
  lat (msec)   : 2=0.02%, 4=0.03%, 10=0.11%, 20=2.71%, 50=10.02%
  lat (msec)   : 100=70.29%, 250=16.61%, 500=0.16%, 750=0.01%
  cpu          : usr=0.15%, sys=0.28%, ctx=94053, majf=0, minf=544
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=99.9%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.1%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwt: total=286337,123263,0, short=0,0,0, dropped=0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=16

Run status group 0 (all jobs):
   READ: bw=8593KiB/s (8799kB/s), 8593KiB/s-8593KiB/s (8799kB/s-8799kB/s), io=1119MiB (1173MB), run=133291-133291msec
  WRITE: bw=3699KiB/s (3788kB/s), 3699KiB/s-3699KiB/s (3788kB/s-3788kB/s), io=481MiB (505MB), run=133291-133291msec

Disk stats (read/write):
  sdb: ios=282335/122545, merge=3682/840, ticks=14128940/4417402, in_queue=18548219, util=100.00%

 

色々出てくるんですけど、分かりやすく指標にするとしたらIOPSの性能部分ではないでしょうか。

Input Output Per Secondの略らしいですけど、1秒間に読み書きできる回数を数値化したものです。

切り出してみると下記のようになっています。 

 

read: IOPS=2148, BW=8593KiB/s (8799kB/s)(1119MiB/133291msec)
write: IOPS=924, BW=3699KiB/s (3788kB/s)(481MiB/133291msec)

 

後述するI/O関連のオプションをチューニングしてみてfioでテストしIOPSの性能値を比較してみるということで改善したかどうかがわかってきそうです。

 

 

I/Oスケジューラをチューニングする

 

I/Oスケジューラとは簡単に言うとディスクへのI/O要求が発生した際にどういうアルゴリズムでその処理を行うかを決めるもので、cfqやnoop、deadlineといったアルゴリズムが存在します。

現在採用しているI/Oスケジューラは下記のコマンドで確認できます。

 

# cat /sys/block/sdb/queue/scheduler 
noop anticipatory deadline [cfq]

 

最近のLinuxではcfqがデフォルトになっていることが多いのですが、cfqはキューにため込んだI/O要求自体のスケジュールを平均になるように処理するようで通常はあんまり気にならなかったりもするんですけど、I/Oが多くなってくるとその辺の処理のオーバーヘッドが顕著になってくる場合もあります。

(多分、ディスクへの書き込みがかなりランダムなのか、それともログのようなシーケンシャルなものが多いのかなどによって変わってくるんだと思いますが)

で、このI/Oスケジューラを変更してみたいと思います。

単純に変更する場合は、

 

# echo noop > /sys/block/sdb/queue/scheduler
# cat /sys/block/sdb/queue/scheduler 
[noop] anticipatory deadline cfq 

 

のようにして変更できます。

上記の場合は、noopに変更しています。

noopは難しいアルゴリズムなしに単純に要求順にI/Oを処理していくものというものです。

この状態で再度fioコマンドでIOPS値を計測してみます。

 

[cfq]
   read: IOPS=583, BW=2336KiB/s (2392kB/s)(1119MiB/490331msec)
  write: IOPS=251, BW=1006KiB/s (1030kB/s)(481MiB/490331msec)

[noop]
   read: IOPS=3288, BW=12.8MiB/s (13.5MB/s)(1119MiB/87073msec)
  write: IOPS=1415, BW=5663KiB/s (5798kB/s)(481MiB/87073msec)

 

noopにすることでかなりパフォーマンスが改善しました。

ただ、これは環境によるので一概にnoopにすればいいというわけではありません。

また、場合によってはdeadlineなどほかのI/Oスケジューラにした方が改善されるかもしれません。

ということで、環境ごとにI/Oスケジューラの変更とfioを使ったパフォーマンスの計測を繰り返してチューニングしてみるのが良いかと思います。

ちなみに、上記のI/Oスケジューラの変更ではOSを再起動すると元に戻ります。

ですので、永続的にI/Oスケジューラを変更する場合はgrub.confなど起動時にオプションに、下記のようにelevatorオプションにてI/Oスケジューラを指定しておきます。

 

kernel /vmlinuz-2.6.32-573.3.1.el6.x86_64 ro root=UUID=2a16f496-9e5a-4312-815a-4c64c5b1fcd4 rd_NO_LUKS rd_NO_MD  KEYBOARDTYPE=pc KEYTABLE=jp106 LANG=ja_JP.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet crashkernel=auto elevator=noop

 

パフォーマンスというとCPUやメモリの使用率などに目を向けがちですが、こういったディスク性能にも大きく引っ張られることもあるので確認やチューニングしてみるのもよいかもしれません。