EBSのボリュームタイプ毎にgit commitのパフォーマンスを計測してみた

## 概要
EBSには4つのボリュームタイプ(高性能順にIO1, GP2, ST1, SC1)がありますが、それぞれ `git commit` がどれ位高速にできるのかパフォーマンスを計測してみました。

## 背景
なぜこんなことをしようと思ったかというと、定期的に同じページをクロールするクローラーを作っていて、クロールした瞬間のHTML or JSONを保存しておき、いつどの内容が変わったのかDiffを見たいという機能要件がありました。
取得したHTMLを何も考えずにS3に投げて、`diff` コマンドで差分を見る方法でも良いのですが、これだとデータ量が膨大になってしまいお金がかかってしまいます。差分だけ保存できて、またその変更履歴も簡単に追えるツール… gitでは!?となって今回技術検証のためにパフォーマンスを計測することにしました。

## 計測方法
計測に使ったファイルは https://github.com/showwin/git_benchmark においてあります。
5000ファイル(1ファイルあたり100B程度)に対して、20000回の変更をランダムに加え、変更毎に`git add && git commit` を実行します。どのファイルに書き込むのかはランダムですが、初期状態でファイルが存在していないため、(およそ)最初の5000コミットは新規ファイル追加のcommit, 残りの15000コミットはファイルの中身が変更されるcommitということになります。


## 検証手順

c4.largeのインスタンスに対して、4種類のボリュームをアタッチします。
HDD系のボリュームは最小容量が500GBなので、それに合わせてSSD側も500GB分のボリュームを作成しました。
<img width="1421" alt="Screen Shot 2018-02-04 at 15.37.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/53708/78db53ae-273d-3d90-d48c-303c0835a1d0.png">
(IO1のIOPSはデフォルト値の25000から変更せずに使います)


[AWSのドキュメント](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ebs-using-volumes.html) を参考にしてボリュームをマウントしていきます。
ホームディレクトリ配下に4種類のボリュームをマウントしました。

```
[ec2-user]$ ls
gp2  io1  sc1  st1
```

```
$ cd [volume_dir]
$ git clone https://github.com/showwin/git_benchmark.git
$ python3 bench_distributed.py
```
によりベンチマークを実行します。

結果はこのようになりました。
<img width="543" alt="Screen Shot 2018-02-04 at 20.09.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/53708/96c3e2ee-b8db-2313-4f4e-3235247f3573.png">

SSDの方はコミット数が増えていくにつれ1秒あたりのコミット数は増えていき、最終的に180commits/secぐらいのパフォーマンスになりました。まだ収束していないので、200ぐらいまでいくかもしれませんね。
逆にHDDの方はコミット数が増えるにつれてパフォーマンスが低下していきました。
(ちなみに遅くなっているのはバーストのクレジットを使い切ったわけではありませんでした。1時間で20%下がるぐらいの速度でバーストクレジットを消費していました。)

ST1が20commits/secぐらいで下限に達したようにも見えるので、アプリケーションの要件的にこれぐらいの速度で許容できるのであれば、HDDでも大丈夫そうです。

ベンチマーカ走行時間(2万コミット完了にかかった時間)はこのようになりました。
<img width="544" alt="Screen Shot 2018-02-04 at 20.04.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/53708/36c975df-5a1b-1913-3c59-67a14ac8e4f4.png">

SSD系とST1の差はこんなもんかなと思っていましたが、ST1とSC1も4倍弱ぐらい差が出る結果となりました。(SC1が思っていたよりも遅かったです)


## おまけ

[showwin/git_benchmark](https://github.com/showwin/git_benchmark) に  `bench_monolithic.py` と `bench_distributed.py` の2つのファイルがおいてありますが、今回2種類の異なるgitレポジトリの作り方でも速度を検証してみました。
`bench_monolithic.py` はルートディレクトリにgitレポジトリを1つだけ作成し、レポジトリ1つで5000ファイルの変更履歴を保存する方法です。

```
├── benchmark_dir # git repo
│   ├── file1
│   ├── file2
│   ├── file3
│   ├── file4
…
``` 

一方の `bench_distributed.py` はそれぞれのファイル毎にレポジトリを作成するようにしました。


```
├── benchmark_dir # NOT git repo
│   ├── dir1  # git repo
│   │   └── file1
│   ├── dir2 # git repo
│   │   └── file2
│   ├── dir3 # git repo
│   │   └── file3
│   ├── dir4 # git repo
│   │   └── file4
…
``` 

io1 で比較するとこれぐらいの差になります。
<img width="680" alt="Screen Shot 2018-02-04 at 17.45.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/53708/8c07f5b8-81b8-9b9f-c9a0-c3a342420fe1.png">


1つのレポジトリで全てを管理する方は2000コミットを超えたぐらいからパフォーマンスが下がっていっていることが分かります。
複数レポジトリの方が高速ですが、もちろんこちらの方ががディスク容量は食います。
比較してみるとこんな感じになりました。

```
bench_distributed: 1.1GB
bench_monolithic: 460MB
```

ちなみに、2万コミット分のファイルをそのまますべで保存すると79MBになりました。
git を使うよりも容量が少なかったです、、笑

すべて1行のみのファイルで、1行内の一部データを変更しているので、gitで管理したらそれはデータ量増えますよね… 
データ量に関する検証も別でやってみたいと思います。