テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(dm-lc report) 物理マシン上での性能測定

まず, dm-lcの仕組みについておさらいします. dm-lcによって作られた仮想ボリューム(LV)にランダムライトが与えられた時, そのLVの中では以下のような処理が行われます.

  1. pre-allocateされたwritebufferにデータを書いてimmediate completionを返します. そのレイテンシは, ライトあたり2.5us程度です.
  2. writebufferが一杯になると, キャッシュのメタデータをセットアップして, キャッシュデバイスに実際に吐くためにqueueします.

writebufferはデフォルトでは合計64MB分用意されています. 例えばwritebufferの大きさが1MBの場合は, 64枚です. 空のwritebufferが枯渇すると, 吐き出し待ち中のwritebufferを再利用するためにblockされるため, キャッシュデバイスへの書き込み速度によってスループットが制限されます. 仮にキャッシュデバイスが十分速ければ, writebufferが枯渇することはありませんから, writebufferへの書き込み速度によってスループットが決定します.

writebufferの大きさについて, writebufferが小さければ, その分だけ頻繁にqueue処理が発生します. これは無料ではありません. メタデータの計算と, メモリライトが必要です.

以下の実験の観点は以下です.
1. 超性能のSSD(MaxSSDと呼びましょう. ioMax社が作った最高のSSDです)を使った場合, ソフトウェアの上限としてどの程度のスループットまで達成可能か.
2. 現実的なSSDであるSamsung 840Proを利用した場合のスループット. writebufferの大きさによってどう変わるか.
3. ソフトウェアの質. SSDへの純粋なシーケンシャルライトに比べて, HDD+SSD(dm-lcによる)のランダムライト性能はどの程度劣化するか.

実験環境は以下です.
* CPU: Intel Core i7-3770
* RAM: 32GB搭載
* カーネルバージョン: 3.9.8 (コンパイルオプションは, Debian Wheezyのものを元にして, 差分についてはEnter押しっぱなし)
* ベースとしたディストリ: Debian 7.1
* SSD: Samsung 840Pro (公称520MB/s)

実験にはfioを使いました. 基本的なスクリプトは以下ですが, 適宜, パラメータを変更しています. ランダムライトの場合は4KBごとだが, SSDのシーケンシャル性能を測りたい時は1MBごとにするなど.

[global]
filename=/dev/mapper/perflv
randrepeat=1
ioengine=libaio
bs=4k
ba=4k
size=16G
direct=1
gtod_reduce=1
norandommap
iodepth=64
stonewall

[perf]
rw=randwrite

結果は以下です. 縦軸はスループット(MB/s).

f:id:akiradeveloper529:20130706200430p:plain

左から, 1,2,.. と番号をつけて説明しましょう.

  1. HDDに対するランダムライトです. 883KB/sでした.
  2. SSDに対するランダムライトです. 202MB/sでした.
  3. HDD+SSD (writebufferサイズは1MB)の性能です. 259MB/sでした. dm-lcを使えば, ネイティブなSSDのランダムライト性能も超えることが出来ます. 理由は, SSDへの書き込みがシーケンシャルだからです. この数値はHDD onlyの293倍です. HDDを並列させてこのスループットを出すには一体いくら積めばよいでしょうか?(単にstripingしただけでは不可能です)
  4. SSDへのシーケンシャルライト性能は266MB/sでした. 3->4の性能劣化は2.6%です. レイテンシが極めて低いことが分かります. 性能が公称と違うのは, 何が設定(AHCIなど)が良くない?
  5. 1MBのwritebufferを20k枚(合計20GB)用意して, MaxSSDを仮想しました. スループットは, 1579MB/sです. お金があれば, IntelPCI-e SSDでの実験をしたいと思っています(パトロン募集. あるいはPCI-e SSDを持ってる人, 代わりに実験お願いします). writeレイテンシは2.5us程度です. 1秒あたり400K個のライトを処理していますから. かなり異常性が高い数値だと思います.
perf: (g=0): rw=randwrite, bs=4K-4K/4K-4K, ioengine=libaio, iodepth=64
2.0.8
Starting 1 process
Jobs: 1 (f=1): [w] [100.0% done] [0K/1547M /s] [0 /396K iops] [eta 00m:00s]
perf: (groupid=0, jobs=1): err= 0: pid=5241
  write: io=16384MB, bw=1579.3MB/s, iops=404276 , runt= 10375msec
  cpu          : usr=22.09%, sys=77.77%, ctx=882, majf=0, minf=19
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.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.1%, >=64=0.0%
     issued    : total=r=0/w=4194367/d=0, short=r=0/w=0/d=0

Run status group 0 (all jobs):
  WRITE: io=16384MB, aggrb=1579.3MB/s, minb=1579.3MB/s, maxb=1579.3MB/s, mint=10375msec, maxt=10375msec

次に, writebufferの大きさのスループットへの影響について. writebufferを小さくすることの長所/短所は以下です.

長所:
1. 頻繁にフラッシュするため, ライトバリアに対して低いレイテンシを期待出来る.
短所:
1. スループットが落ちる (#1)
2. メタデータ領域が増えるため, 初期化に時間がかかる (#2). ディスク容量の利用効率が落ちる(もっとも悪い場合は半分をメタデータに使うことも可能. writebuffer 1MBならば, 0.4%).
3. インメモリメタデータが増える.

このように, 効率の点からいうと短所が多いのですが, 実際のアプリケーションではデータベースを始めとして, ライトバリアを頻発します(XFSなども). 従って, どちらかというと, writebufferは小さくするのが現実的だと私は考えています. 現実的なのは32KBか64KBだと思いますので, このうち32KBで実験を行います.

まず, 上記#1の観点.

writebufferサイズ 個数 スループット(MB/s)
1MB 64 259
32KB 2K 184

fairnessのため, writebufferの総量は統一しました. 結果として, 29%のスループット低下となりました. 性能だけ見れば, 十分に高いと考えます. むしろ問題は, メタデータの量となってくるのではないかと思いますが, dm-lcを利用する場合, キャッシュデバイスは現実的には多く要りませんから, だとすると, 29%の性能低下だけがペナルティとなり, これは悪くありません. しかし実際には, 想定するアプリケーションによって設定を変更することが必要です. dm-lcが無駄なアプリケーションも存在します. There is no silver bulletです.

次に#2の観点.

writebufferサイズ format時間(sec) resume時間(sec)
1MB 1.25 4.38
32KB 36.8 113.3

実際にフォーマットしたSSDの大きさは24GBです. メタデータ領域の数が 32倍になっているので, 時間もそのくらいに増えています. この前のブログで, formatに関するwrite効率を改善したという話をしましたが, 次はresume(read)が問題になっています. ある部分を最適化したら次は違うボトルネックとなるというのはよくあることです. 十分に速いと思うので, これ以上は改善しません. もとの実装であれば, このケースであっても合計で15分くらいかかっていたかも知れません.

以上です.

最後に, この実験の犠牲になったVMたち(Hercules, ARMTest, PlayServer)を紹介させてください. 測定において, キャッシュデバイス(/dev/sdd2)を手動でblkdiscardすることがありました. ここで誤って, 彼らが入ったパーティション(/dev/sdd1)も巻き込んでしまい, 彼らがこの世から消えてしまいました. '2'を忘れて, /dev/sddを消去してしまったのです. 彼らのうち特に, Herculesは, 開発デバッグ用として使っていました. これを再構築するのは大変ですから, 開発は少し中断します. 技術の進歩のために, 人やモノの犠牲は必要と思いますが, 彼らの死が無駄にならないようにdm-lcを最高のリリース形に持っていく使命があります. 意識がなくなってきましたが頑張ります.