fioを使ってストレージの性能を計測してみた

ストレージの性能測定を行ったことがなく、調べてみたらfioというコマンドで色々計測できるようなのでやってみたときのメモ

# 参考

+ [噂の高速SSDを積んだAmazon EC2インスタンスのI/Oベンチマークをとってみた](http://d.hatena.ne.jp/rx7/20120727/p1)

+ [EXT4 vs XFS vs Btrfs vs ZFSをfioでベンチマークを取ってみました。](http://qiita.com/sion_cojp/items/bef955bba3dbf9d603f8)

+ [LinuxのI/OベンチマークでCrystalDiskMarkと同等の計測をfioで実現](https://www.winkey.jp/article.php/20110310142828679)

+ [さくらのVPS の SSD 1Gプランが出たので、iops を計測してみました。ついでに、お名前.com VPS(KVM) 1Gプランとも比較してみました。(2)](http://server-setting.info/blog/sakura-vps-ssd-iops2.html)
+ [Premium Storage を使ってみよう ～IOPS とスループットの関係～](http://blog.ryukiy.net/2015/06/12/premium-storage-part2/)

# 環境

+ ホストOS->Mac OSX
+ 検証用VM Linuxマシン->Ubuntu14.04

# 調査対象のVM環境

```bash
# ubuntu14.04
$cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04 LTS"

# ファイルシステム
$df -Th
df -Th
Filesystem     Type      Size  Used Avail Use% Mounted on
/dev/sda1      ext4       40G  1.1G   37G   3% /
none           tmpfs     4.0K     0  4.0K   0% /sys/fs/cgroup
udev           devtmpfs  997M   12K  997M   1% /dev
tmpfs          tmpfs     201M  356K  200M   1% /run
none           tmpfs     5.0M     0  5.0M   0% /run/lock
none           tmpfs    1002M     0 1002M   0% /run/shm
none           tmpfs     100M     0  100M   0% /run/user
vagrant        vboxsf    233G  209G   24G  90% /vagrant

# メモリ
$free -m
             total       used       free     shared    buffers     cached
Mem:          2002        293       1708          0        110         37
-/+ buffers/cache:        145       1856
Swap:            0          0          0
```

# そもそもストレージの性能評価とは

以下の3つの指標でストレージのパフォーマンスとするようです。

+ IOPS
+ レスポンスタイムもしくはレイテンシ
+ スループット

1.IOPS(Input Output Per Second)
->秒間のI/O数
->IOPS=アームの動く時間+回転待ち時間+実際の読み書き時間
->I/Oのサイズが大きくなるとIOPSが減る
->ランダムI/Oの性能を見ると場合にはこの値を見ると良い

2.レスポンスタイム(応答時間)もしくはレイテンシ
->I/O要求をストレージシステムが受けてから応答が返ってくるまでの時間
->個々のI/O要求へのレスポンスタイムである点が大切。レスポンスタイムが多少遅くても並行して処理を行うことができれば全体の仕事量は増やすことができる

3.スループット
->時間当たりの仕事量

自分の中でIOPSとスループットの意味が違うのは分かりつつ、どのような関係かというのは以下の記事が分かりやすかったです。

[Premium Storage を使ってみよう ～IOPS とスループットの関係～](http://blog.ryukiy.net/2015/06/12/premium-storage-part2/)

IOPSが高くても一度に書き込む・読み込むサイズが小さければスループットは高くならないよというのが良く理解できました。

ストレージ選定の評価軸としてアプリケーションが細かいデータを頻繁に読み書きする場合にはIOPSを確認した方がいいでしょうし、大きなデータを書き込む場合にはスループットを確認した方が良いという点が違います。

また、クラウドではネットワークの帯域幅も大切なポイントです。
仮想マシンとストレージがネットワーク経由で接続されている場合にネットワークの帯域幅が狭い場合、いくらストレージの性能が良い場合でも転送できるデータサイズが少なく、スループットやレスポンスタイムが大きくなってしまうことが考えられます。


# やってみた

## インストール

まずはコマンドをインストール

```bash
$sudo apt-get install fio
$fio --version
fio-2.1.3
```

## 計測してみる(シーケンシャル読み込み)

まずはシーケンシャルな読み込みのIOPSを計測します。
fioコマンド実行時にオプション引数を指定することで様々なパラメーターを指定できます。

参考サイトを参考に指定したオプション引数の内容を記載します。

+ filename->指定したファイルパスにテスト用のファイルを作成する
+ direct->trueの場合にはnon-buffered I/Oで計測する。バッファは使いたくないので1
+ rw->I/Oのパターンを指定。シーケンシャルリード、シーケンシャルライトなど。readはシーケンシャルリードを示す
+ bs->I/Oで利用するブロックのサイズ
+ size->I/Oで利用する合計のサイズ
+ numjobs->パフォーマンス計測時のプロセス/ストッドの数
+ runtime->実行時間の限度。秒単位。
+ group_reporting->有効にするとnumjobsで複数処理した結果をまとめて表示する
+ name=ジョブの名前

では実際に計測してみます。

```bash
$fio -filename=/tmp/test2g -direct=1 -rw=read -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1
file1: (g=0): rw=read, bs=4K-4K/4K-4K/4K-4K, ioengine=sync, iodepth=1
...
file1: (g=0): rw=read, bs=4K-4K/4K-4K/4K-4K, ioengine=sync, iodepth=1
fio-2.1.3
Starting 64 processes
file1: Laying out IO file(s) (1 file(s) / 2048MB)
Jobs: 64 (f=64): [RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR] [36.4% done] [54113KB/0KB/0KB /s] [13.6K/0/0 iops] [Jobs: 64 (f=64): [RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR] [50.0% done] [53341KB/0KB/0KB /s] [13.4K/0/0 iops] [Jobs: 64 (f=64): [RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR] [60.0% done] [55818KB/0KB/0KB /s] [13.1K/0/0 iops] [Jobs: 64 (f=64): [RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR] [70.0% done] [55283KB/0KB/0KB /s] [13.9K/0/0 iops] [Jobs: 64 (f=64): [RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR] [80.0% done] [55947KB/0KB/0KB /s] [13.1K/0/0 iops] [Jobs: 64 (f=64): [RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR] [90.0% done] [56055KB/0KB/0KB /s] [14.2K/0/0 iops] [Jobs: 64 (f=64): [RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR] [100.0% done] [55670KB/0KB/0KB /s] [13.1K/0/0 iops] [eta 00m:00s]
file1: (groupid=0, jobs=64): err= 0: pid=2446: Mon Apr 25 00:22:00 2016
  read : io=554300KB, bw=55358KB/s, iops=13839, runt= 10013msec
    clat (usec): min=0, max=946305, avg=4611.38, stdev=4571.33
     lat (usec): min=0, max=946306, avg=4612.43, stdev=4580.94
    clat percentiles (usec):
     |  1.00th=[   75],  5.00th=[   78], 10.00th=[   95], 20.00th=[ 1816],
     | 30.00th=[ 3984], 40.00th=[ 4448], 50.00th=[ 4704], 60.00th=[ 5024],
     | 70.00th=[ 5344], 80.00th=[ 6240], 90.00th=[ 7840], 95.00th=[ 8896],
     | 99.00th=[12864], 99.50th=[16512], 99.90th=[52480], 99.95th=[55552],
     | 99.99th=[103936]
    bw (KB  /s): min=   43, max= 1005, per=1.57%, avg=867.01, stdev=64.61
    lat (usec) : 2=0.07%, 4=0.01%, 10=0.10%, 20=0.05%, 50=0.12%
    lat (usec) : 100=12.32%, 250=3.98%, 500=0.19%, 750=0.29%, 1000=0.50%
    lat (msec) : 2=3.02%, 4=9.55%, 10=67.10%, 20=2.32%, 50=0.23%
    lat (msec) : 100=0.13%, 250=0.01%, 1000=0.01%
  cpu          : usr=0.13%, sys=1.39%, ctx=116585, majf=0, minf=1952
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 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.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=138575/w=0/d=0, short=r=0/w=0/d=0

Run status group 0 (all jobs):
   READ: io=554300KB, aggrb=55358KB/s, minb=55358KB/s, maxb=55358KB/s, mint=10013msec, maxt=10013msec

Disk stats (read/write):
  sda: ios=137552/2, merge=59/1, ticks=22572/0, in_queue=21828, util=98.21%
```

まずread行を見てみます。

+ io->全体のIO量
+ bw->帯域幅、スループット
+ iops->IOPS
+ runt->実行時間(run time)

となっています。

上記例であればIOPSは13839と記載されています。つまりこのストレージは1秒間で4KBのブロックを13839回読み込みができるということを意味し、4KB*13839=55356KBが1秒間にやりとりできるデータ量ということになります。read行のbwがまさにこのデータ量で1秒間のスループットとなります。

また、レイテンシも確認できます。
下の方のlat(usec)、lat(msec)などの行でで一番割合の高いものとします。
msecがミリ秒、usecがマイクロ秒を表しています。
上記例であれば、10=67.10%が一番割合が高いレイテンシと言えるかと思います。

## 計測してみる(残り)

やり方は分かったのでどんどん計測して行きます。
先ほど指定したオプション引数の-rwオプションを変更しつつ、計測します。

```bash
# シーケンシャルライト
$fio -filename=/tmp/test2g -direct=1 -rw=write -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1
・・・
write: io=417236KB, bw=41620KB/s, iops=10404, runt= 10025msec

・・・
lat (usec) : 2=0.02%, 20=0.01%, 50=0.20%, 100=83.77%, 250=12.55%
・・・

# ランダムリード
$fio -filename=/tmp/test2g -direct=1 -rw=randread -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1
・・・
read : io=156640KB, bw=15639KB/s, iops=3909, runt= 10016msec
・・・
lat (msec) : 4=0.10%, 10=6.53%, 20=60.80%, 50=31.83%, 100=0.74%
・・・

# ランダムライト
$fio -filename=/tmp/test2g -direct=1 -rw=randwrite -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1
・・・
write: io=193544KB, bw=19304KB/s, iops=4826, runt= 10026msec
・・・
lat (usec) : 2=0.01%, 20=0.01%, 50=0.03%, 100=5.30%, 250=87.14%
・・・
```

次にスループットを計測するためにブロックサイズを大きくして確認を行います。

```bash
# シーケンシャルリード
$fio -filename=/tmp/test2g -direct=1 -rw=write -bs=32m -size=2G -numjobs=16 -runtime=10 -group_reporting -name=file1
・・・
write: io=8128.0MB, bw=705943KB/s, iops=21, runt= 11790msec
・・・
lat (msec) : 50=9.18%, 100=3.16%, 250=2.85%, 500=45.57%, 750=17.09%
・・・

# シーケンシャルライト
$fio -filename=/tmp/test2g -direct=1 -rw=write -bs=32m -size=2G -numjobs=16 -runtime=10 -group_reporting -name=file1
・・・
write: io=9184.0MB, bw=766331KB/s, iops=23, runt= 12272msec
・・・
lat (msec) : 50=4.53%, 100=6.79%, 250=3.02%, 500=50.94%, 750=13.96%
・・・


# ランダムリード
$fio -filename=/tmp/test2g -direct=1 -rw=randread -bs=32m -size=2G -numjobs=16 -runtime=10 -group_reporting -name=file1
・・・
read : io=4640.0MB, bw=399543KB/s, iops=12, runt= 11892msec
・・・
lat (msec) : 2000=45.24%
・・・

# ランダムライト
$fio -filename=/tmp/test2g -direct=1 -rw=randwrite -bs=32m -size=2G -numjobs=16 -runtime=10 -group_reporting -name=file1
・・・
write: io=2816.0MB, bw=240881KB/s, iops=7, runt= 11971msec
・・・
lat (msec) : 2000=6.19%, >=2000=49.48%
・・・

```

## 結果のまとめ


IOPSの性能を計測するために4KB固定の読み込み書き込みをした結果を以下に示します。

|種類|IOPS|スループット(KB/S)|スループット(MB/S)|レイテンシ|
|:-----------|:------------|:------------|:------------|:------------|
| sequential read|13839|44358|44|10msec|
| sequential write|10401|41620|41|100usec|
| random read|3909|15639|15|10msec|
| random write|4826|19304|19|250usec|


また、以下に32MBにブロックサイズを変更し、スループットを確認した時の結果を示します。

|種類|IOPS|スループット(KB/S)|スループット(MB/S)|レイテンシ|
|:-----------|:------------|:------------|:------------|:------------|
| sequential read|21|705943|705|500msec|
| sequential write|23|766331|766|500msec|
| random read|12|399543|399|2000msec|
| random write|7|240881|240|>=2000msec|


一つ目の結果はIOPSは高いですが、ブロックサイズが4KBと小さいのでスループットは少ないです。
２つ目はIOPSは低いですが、ブロックサイズが32MBと大きいのでスループットは高くなっています。

レイテンシに関してはランダムな読み込み・書き込みの方がシーケンシャルな読み込み・書き込みより高い値になるというぐらいであまり規則性は見えませんでした。。。（割合の高いものをピックアップしているだけなのであまりいい計測方法ではないのかも。。）