Skip to content

Benchmark

Takeru Ohta edited this page Oct 21, 2018 · 24 revisions

CannyLSのベンチマーク結果。

CannyLSが主眼に置いている「大容量HDD上でのランダムアクセスワークロード」での性能を確認することが目的。

測定内容

  • 1TB分のデータに対して各種操作を逐次的に実行し、その所要時間(レイテンシ)を計測する
    • ※ 理想的には、もう一桁以上は大きなサイズの方が望ましいが、今回は時間の都合上1TBとした
  • キーは16バイトのランダムバイト列
  • 値は以下のそれぞれのサイズのランダムバイト列:
    • 100KB
    • 1MB
    • 10MB
  • 測定対象操作:
    • PUT(新規)
    • PUT(更新)
    • GET
    • DELETE
    • LOAD ※ 1TB分のデータを有するKVSの起動時間
    • ※ なお時間の関係上、PUT(新規)LOAD以外は、格納済みエントリ全体の中から、無作為に選択した1%、に対して操作を実行している
  • 各測定の前には、OSのキャッシュ(ページキャッシュ及びslabキャッシュ)はクリアしておく
    • 具体的にはsudo sh -c "echo 3 > /proc/sys/vm/drop_caches"を実行する
  • 比較のためにCannyLS以外の組み込みKVS(後述)に対しても、同様の測定を行う
  • データの保存先には、KSV毎および値のサイズ毎、に別々のHDDを使用する
  • 測定ツールとしてはekvsb(v0.0.8)を使用

測定環境

  • CPU: Intel(R) Xeon(R) CPU E5-2630L v3 @ 1.80GHz
  • RAM: 64GB
  • HDD:
    • 容量: 3TB
    • 回転数: 15,000rpm
    • ファイルシステム: XFS
  • OS: Scientific Linux 7.2

測定対象

以下の四つの組み込みKSV(ではないものもあるが)に対して、測定を実施する:

  • cannyls:
  • cannyls(sync):
    • CannyLSのジャーナルメモリバッファを無効にしたもの
    • プロセスクラッシュ時の整合性保証は最大化されるが、その分性能は劣化する
    • 測定コマンド: ekvsb run cannyls --capacity 2048GiB --journal-sync-interval 0 /path/to/lusf-file
  • xfs:
    • xfsフォーマットのファイルシステムをKVS的に利用したもの
    • 具体的には、以下のように利用している:
      • 一エントリが一ファイル
      • 単一ディレクトリ内の大量のファイルが作成されることを避けるために、キーのハッシュの上位三バイトを用いて、以下のようなパスを生成している
        • {ハッシュの一バイト目}/{ハッシュの二バイト目}/{ハッシュの三バイト目}/{キー名}
    • xfsがフォーマットとして選択された強い理由はない(たまたま測定環境がxfsだったため)
    • 測定コマンド: ekvsb run builtin::fs /path/to/root-dir/
  • rocksdb:

注意

  • XFSおよびRocksDBの結果は、あくまでも参考として載せられているものであり、これら自体の最大性能を示すものではない
    • 両者ともに特にチューニング等は行っていない
  • 特にRocksDBに関しては「A Persistent Key-Value Store for Flash and RAM Storage」とREADMEで銘打たれており、そもそもHDD向けでは無いことは留意しておく必要がある
  • Rustから利用可能なより適切な、HDDを対象として組み込みKVSが存在する場合には、比較対象をそちらに変更する可能性がある

測定結果

各操作の性能測定結果:

  • レイテンシの単位は秒
  • PUTおよびGETに関しては「秒間に処理されたデータ量(MB/秒)」を示すスループットカラムが存在
  • DELETEに関しては「秒間に処理された操作数」を示す秒間処理数カラムが存在
  • 図の掲載順は左からcannyls, cannyls(sync), xfs, rocksdb
  • 図のY軸(レイテンシ)の上限は五秒で固定

操作: PUT(新規)

測定および結果生成コマンド(データサイズが100KBの場合):

$ ekvsb workload PUT --count 10000000 --key-size 16 --value-size 100KiB --seed foo > workload.put.100kb.json
$ ${対象KVS用の測定コマンド} < workload.put.100kb.json > result.put.100kb.json
$ ekvsb summary < result.put.100kb.json
$ ekvsb plot png put.100kb.png --title "PUT(new): kvs=${KVS名}, datasize=100KB" --y-max 5 < result.put.100kb.json

データサイズ: 100KB

KVS スループット レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 249.235 0.000358 0.001475 1.261304
cannyls(sync) 137.087 0.000519 0.005121 1.189983
xfs 9.367 0.000125 0.210620 9.309418
rocksdb 2.295 0.029796 0.259761 703.077684
PUT(new): kvs=cannyls, datasize=100KB PUT(new): kvs=cannyls(sync), datasize=100KB PUT(new): kvs=xfs, datasize=100KB PUT(new): kvs=rocksdb, datasize=100KB

レイテンシの中央値に関してはxfsが一番小さいが、それ以外ではcannylsが良好な結果を示していた。 rocksdbはDBのサイズが増加するにつれて性能が劣化し、またレイテンシのバラつきもかなり大きなものとなっていた。

データサイズ: 1MB

KVS スループット レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 217.070 0.004342 0.012747 1.591087
cannyls(sync) 142.533 0.006241 0.022470 1.369462
xfs 54.699 0.000699 0.473626 2.605536
rocksdb 2.954 0.235579 1.956971 960.785539
PUT(new): kvs=cannyls, datasize=1MB PUT(new): kvs=cannyls(sync), datasize=1MB PUT(new): kvs=xfs, datasize=1MB PUT(new): kvs=cannyls, datasize=1MB

全体的な傾向はデータサイズが100KBの時と同様だが、xfsのスループットに関しては今回の方が格段に向上している。

データサイズ: 10MB

KVS スループット レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 224.392 0.043889 0.075065 0.498525
cannyls(sync) 224.540 0.043010 0.084834 1.663457
xfs 134.809 0.006345 1.256966 2.484090
rocksdb 4.037 1.733949 11.826268 790.836380
PUT(new): kvs=cannyls, datasize=10MB PUT(new): kvs=cannyls(sync), datasize=10MB PUT(new): kvs=xfs, datasize=10MB PUT(new): kvs=cannyls, datasize=10MB

全体的な傾向はデータサイズが1MBの時と似ているが、xfsのスループットが今回も向上しているのと、cannylsのジャーナルバッファ有無での性能差がほぼなくなっているのが特徴。

操作: PUT(更新)

測定および結果生成コマンド(データサイズが100KBの場合):

$ ekvsb workload PUT --count 100000 --population-size 10000000 --shuffle qux --key-size 16 --seed foo --value-size 100KiB > workload.update.100kb.json
$ ${対象KVS用の測定コマンド} < workload.update.100kb.json > result.update.100kb.json
$ ekvsb summary < result.update.100kb.json
$ ekvsb plot png update.100kb.png --title "PUT(update): kvs=${KVS名}, datasize=100KB" --y-max 5 < result.update.100kb.json

※ 事前にPUT(新規)の測定が実施されていることが前提

データサイズ: 100KB

KVS スループット レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 57.696 0.001473 0.010359 0.037559
cannyls(sync) 34.065 0.001955 0.016928 1.185657
xfs 2.692 0.031768 0.075660 3.659143
rocksdb 9.662 0.010346 0.037739 0.182336
PUT(update): kvs=cannyls, datasize=100KB PUT(update): kvs=cannyls(sync), datasize=100KB PUT(update): kvs=xfs, datasize=100KB PUT(update): kvs=rocksdb, datasize=100KB

新規PUTに比べるとスループットは大幅に下がっているが、それでもcannylsが一番レイテンシも小さく安定している。 またrocksdbの性能が良くなっているのも特徴(件数を新規PUTと同様にすれば後半で傾向が変ってくる可能性もあるが未確認)。

データサイズ: 1MB

KVS スループット レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 222.293 0.004191 0.014692 0.035776
cannyls(sync) 163.345 0.006310 0.019325 0.522372
xfs 21.004 0.043746 0.097234 2.283350
rocksdb 4.751 0.194194 0.468607 0.470332
PUT(update): kvs=cannyls, datasize=1MB PUT(update): kvs=cannyls(sync), datasize=1MB PUT(update): kvs=xfs, datasize=1MB PUT(update): kvs=cannyls, datasize=1MB

データサイズが100KBの場合に比べて、全体的にスループットが向上する形となっている。ただしrocksdbだけは逆にスループットが下がっている。 一回のPUTのレイテンシに関しては、データサイズが増えた分だけ長くなっている。

データサイズ: 10MB

KVS スループット レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 362.061 0.010537 0.072840 0.081998
cannyls(sync) 334.785 0.022547 0.077606 0.109275
xfs 87.121 0.114569 0.327141 0.558513
rocksdb 3.513 2.065413 14.258073 27.805194
PUT(update): kvs=cannyls, datasize=10MB PUT(update): kvs=cannyls(sync), datasize=10MB PUT(update): kvs=xfs, datasize=10MB PUT(update): kvs=cannyls, datasize=10MB

一つ前の結果と同様に、データサイズが大きくなったことで全体的にスループットは向上しているが、rocksdbだけは大幅に性能が落ちている。

操作: GET

測定および結果生成コマンド(データサイズが100KBの場合):

$ ekvsb workload GET --count 100000 --population-size 10000000 --shuffle bar --key-size 16 --seed foo > workload.get.100kb.json
$ ${対象KVS用の測定コマンド} < workload.get.100kb.json > result.get.100kb.json
$ ekvsb summary < result.get.100kb.json
$ ekvsb plot png get.100kb.png --title "GET: kvs=${KVS名}, datasize=100KB" --y-max 5 < result.get.100kb.json

※ 事前にPUT(新規)の測定が実施されていることが前提

データサイズ: 100KB

KVS スループット レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 9.916 0.009848 0.015516 0.072573
cannyls(sync) 10.170 0.009563 0.015115 0.192162
xfs 3.125 0.025806 0.060545 4.122876
rocksdb 2.372 0.018690 0.849928 3.852956
GET: kvs=cannyls, datasize=100KB GET: kvs=cannyls(sync), datasize=100KB GET: kvs=xfs, datasize=100KB GET: kvs=rocksdb, datasize=100KB

cannylsのジャーナルメモリバッファはGET操作には影響を与えないので、今回は有り無しどちらの場合でも似たような結果となっている。 各KVSの結果はそれぞれ特徴的で「cannylsは全体的に安定」、「xfsは全体的にバラついている」、「rocksdbは最初はバラつきが激しいがしばらくしたら低いレイテンシで安定(ページキャッシュの影響?)」といった傾向となっている。

データサイズ: 1MB

KVS スループット レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 63.331 0.015778 0.021745 0.040909
cannyls(sync) 63.949 0.015586 0.021668 0.095330
xfs 19.033 0.048192 0.082145 2.594533
rocksdb 3.142 0.056083 2.198023 4.455123
GET: kvs=cannyls, datasize=1MB GET: kvs=cannyls(sync), datasize=1MB GET: kvs=xfs, datasize=1MB GET: kvs=rocksdb, datasize=1MB

大まかな傾向はデータサイズが100KBの時と同様だが、サイズ増加の影響でスループットは全体的に向上している。また、rocksdbのレイテンシが安定するまでに要する操作回数も多くなっている。

データサイズ: 10MB

KVS スループット レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 137.349 0.072592 0.083659 0.098612
cannyls(sync) 142.484 0.069988 0.081944 0.094999
xfs 57.596 0.167162 0.269709 1.045060
rocksdb 11.070 0.858279 2.338944 2.997740
GET: kvs=cannyls, datasize=10MB GET: kvs=cannyls(sync), datasize=10MB GET: kvs=xfs, datasize=10MB GET: kvs=rocksdb, datasize=10MB

これも傾向としては、データサイズが100KBから1MBに変わった場合と同様(スループットは向上し、レイテンシは長くなり、rocksdbの性能劣化が目立つ)。

操作: DELETE

測定および結果生成コマンド(データサイズが100KBの場合):

$ ekvsb workload DELETE --count 100000 --population-size 10000000 --shuffle baz --key-size 16 --seed foo > workload.delete.100kb.json
$ ${対象KVS用の測定コマンド} < workload.delete.100kb.json > result.delete.100kb.json
$ ekvsb summary < result.delete.100kb.json

※ 事前にPUT(新規)の測定が実施されていることが前提

なおDELETEに関しては、表を見れば十分に傾向が把握可能なため、図の掲載は省略している。

データサイズ: 100KB

KVS 秒間処理数 レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 5,838.170 0.000169 0.000181 0.030402
cannyls(sync) 4,810.811 0.000169 0.000355 0.136506
xfs 38.753 0.018704 0.061354 3.680779
rocksdb 118,899.689 0.000008 0.000015 0.001429

rocksdbが圧倒的に速く、逆にxfsは大幅に遅くなっている。

cannylsはその中間で、ジャーナルバッファが有効になっている方が性能は良いが、無効な場合でもそこまで極端に劣化している訳ではない。 なお現状のekvsbの実装では、cannylsが非同期インタフェースを提供している関係上、DELETE操作を発行した後に100us間隔でその結果をポーリングしているので、その間隔をもう少し短くすれば、測定上のレイテンシがもっと向上する可能性はある。

データサイズ: 1MB

KVS 秒間処理数 レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 5,854.403 0.000169 0.000181 0.000651
cannyls(sync) 4,847.301 0.000167 0.000340 0.029850
xfs 34.177 0.026111 0.061702 2.431488
rocksdb 308,862.363 0.000003 0.000005 0.000527

データサイズが100KBの場合と同様。

データサイズ: 10MB

KVS 秒間処理数 レイテンシ(中央) レイテンシ(99%点) レイテンシ(最大)
cannyls 5,824.257 0.000168 0.000186 0.000887
cannyls(sync) 4,971.541 0.000168 0.000487 0.000502
xfs 44.313 0.020823 0.063784 0.098553
rocksdb 95,195.420 0.000006 0.000015 0.001647

データサイズが100KBの場合と同様。

操作: LOAD

以下の方法で、各KVSの起動に要する時間を計測:

$ time echo '[]' | ${対象KVS用の測定コマンド}
KVS データサイズ 要素数 起動時間
cannyls 100 KB 10,000,000 18.840
cannyls 1 MB 1,000,000 1.900
cannyls 10 MB 100,000 0.181
cannyls(sync) 100 KB 10,000,000 19.347
cannyls(sync) 1 MB 1,000,000 1.918
cannyls(sync) 10 MB 100,000 0.173
xfs 100 KB 10,000,000 0.018
xfs 1 MB 1,000,000 0.033
xfs 10 MB 100,000 0.029
rocksdb 100 KB 10,000,000 170.773
rocksdb 1 MB 1,000,000 178.919
rocksdb 10 MB 100,000 112.335
rocksdb(ページキャッシュ未クリア) 100 KB 10,000,000 2.411
rocksdb(ページキャッシュ未クリア) 1 MB 1,000,000 1.825
rocksdb(ページキャッシュ未クリア) 10 MB 100,000 1.953

ファイルシステムに関しては特別な起動処理は不要なので、当然xfsが一番短い結果となっている(実質上、コマンドの起動・停止時間そのもの)。

rocksdbは事前にページキャッシュに載っているかどうかで、大幅に起動時間に差が出ていた。ページキャッシュに必要なデータが事前に載っている場合には、かなり良好な結果となっているが、逆にページキャッシュがクリアされている場合の起動時間はこの中では一番長いものとなっている。また、起動時間はDBのサイズにのみ影響を受けているように見える。

cannylsは、起動時間が要素数に比例して長くなっている。おそらく要素数がもう一桁増えれば、rocksdb(ページキャッシュ無)よりも起動時に時間が掛かるようになるのではないかと思われる。

さいごに

個々の測定結果を見てみると必ずしもCannyLSが全てにおいて一番優れていた訳ではないが、CannyLSが目指している「安定した低レイテンシの提供」という点においては、現時点でもそれなりに達成できていることは示せたのではないかと思う。