NVMeディスクで書き込みより読み出しが遅いのはなんでだろう（解決編）

[前々回](https://qiita.com/mzaki/items/0fb9e395f4a32a7b3549)、[前回](https://qiita.com/mzaki/items/183e2d5751f4bf4c9450)の続き。

#動機
最近NVMeディスクをインストールしたのだけど、軽い気持ちで性能測定したら書き込みはカタログスペック通りなのに、読み出しが書き込みより遅かった。

#結論
だいたいSLCキャッシュのせい。

お急ぎの場合は[まとめ](#まとめ)だけどうぞ。

#前提
HP Z440 Workstation  

* CPU: Xeon E5-1620v3 (3.50GHz)
* MEM: 64GB DDR4 (2133MHz, ECC, Registered)
* NVMe: [WD Black PCIe SSD 256GB (WDS256G1X0C)](http://products.wdc.com/library/SpecSheet/ENG/2879-800086.pdf)

OSはFreeBSD 11.1-RELEASE-p10で、NVMeの他にZFSでミラー構成になったHDDが付いている。[デバイスの詳細](https://qiita.com/mzaki/items/0fb9e395f4a32a7b3549#デバイスの詳細)は前々回参照。

#SLCキャッシュの調査
今回のデバイスはSandiskのTLC NANDチップを使っておりSLCキャッシュを挟むことで性能を改善させているという。

##書き込みへの影響
SLCキャッシュの詳細がわからないが書き込み性能に関わっているはずなので、その動作を検証してみる。

```shell:write.sh
#!/bin/sh
for time in `seq 1 15`
do
  ./nvmecontrol format nvme0ns1
  nvmecontrol perftest -n $1 -i bio -o write -s 131072 -t $time nvme0ns1
done
```

ちょっとトリッキーだが、IOサイズ128KiBでテスト時間を1秒刻みで伸ばしていき、累積書き込み量の差分から毎秒の書き込み性能を求めてプロットしたのが以下。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253516/f2df90d9-db7c-beab-16ba-6d90a24312d4.png)

最初は690MB/sとほぼカタログスペックであるが、途中で210MB/sに落ちている。このとき累積書き込み量は2.7GBくらいで、だいたい3GBくらいがSLCキャッシュとして利用されているようだ。[前回](https://qiita.com/mzaki/items/183e2d5751f4bf4c9450#デバイス特性の把握)、シングルスレッドで書き込み性能が400MB/sで頭打ちになっていたが、これはテスト時間を10秒決め打ちでやっていたため、後半で性能が落ちていることを反映したものである。

これがいつどのように回復するのかがわからない。ホストからのアクセスが終わったあと順次TLC領域に書き出しているのならば、3000/210=15秒くらいで吐き出し終わるはず。だが20秒待っただけでは回復せず、気がつくと回復している。再現性のある条件が見つかっていない。

##読み出しへの影響
キャッシュが読み出しへ影響するか同様に調べてみる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253516/8823340d-7abe-bd13-ff5a-934462fbfa00.png)

安定して1340MB/sで読み出せており、カタログスペックには届かないがシングルスレッドでは帯域を埋められないためである。ただ直前に低レベルフォーマットをしているので、これは全て未使用セクタを読んだ結果であり、現実的には意味が全くない。前回、書き込んでからだと読み出し性能が大幅に落ちており、使用済みセクタで性能が劣化すると考えたのだが、SLCキャッシュがなにか影響しているという可能性を検討してみた。具体的には、まず特定時間だけ書き込んでからテスト時間だけ読み出したときの累計読み出し量を、テスト時間を伸ばしながら計測して読み出し性能の推移を求めている。

```shell:write-read.sh
#!/bin/sh
for wt in `seq 1 2 9`
do
  echo "[${wt} sec write]"
  for rt in `seq 1 15`
  do
    ./nvmecontrol format nvme0ns1
    nvmecontrol perftest -n 1 -i bio -o write -s 131072 -t $wt nvme0ns1 >/dev/null
    nvmecontrol perftest -n 1 -i bio -o read  -s 131072 -t $rt nvme0ns1
  done
done
```

結果を先ほどと同様にプロットしたのが以下のグラフ。Wn→Rはn秒書き込みをした後読み出した場合の意味である。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253516/0cd8c04e-138b-1b6f-7d59-f907a7c4cc32.png)

たしかに性能低下があるが、しかし書き込んだ量に応じた時間の経過とともに回復することがわかった。1秒または3秒書き込んでから読み出すと、最初は490MB/sしか読み出せないがしばらくすると1340MB/sに回復する。5秒以上書き込んだ場合は最初はさらに低速にしか読み出せないが、すぐに490MB/sになりしばらくたつと1340MB/sに回復する。前回は「使用済みセクタの読み出し性能が低下する」と考えていたが、時間の経過により読み出し性能は未使用セクタと同等に戻るので、別の理由を考えないといけない。

5秒以上書き込んだ場合は書き込みキャッシュが溢れているわけだが、単に書き込みキャッシュが占有されたために読み出し性能に影響が出たとは考えにくい。もしそうなら5秒以上は同じように性能が推移するはずであるが、実際には7秒、9秒と書き込み量を増やすとそれに応じて性能回復が遅れている。ここで考えられるのは、書き込みキャッシュの内容をTLC領域に書き出すのと、TLC領域からデータを読み出すのが競合して性能が低下しているという可能性である。もっともこれ以上は調べようがない。

#まとめ
* 書き込み性能はSLCキャッシュのおかげで690MB/sに達している
* SLCキャッシュが溢れるとシングルスレッドでの書き込み性能は210MB/sに落ちる
* 読み込み性能はSLCキャッシュに直接関係ないが、SLCキャッシュに書かれた内容をTLC領域に書き出す際には、競合して読み出し性能が490MB/sに落ちる

以上の結果、直前に書き込んでから読み出す場合には、書き込みより読み出しが遅いという現象が現れる。

最後までわからないのはSLCキャッシュからTLC領域への書き出し条件であるが、これは詮無いかな。

#付録
最初の疑問に対する答えはある程度出たが、途中で調べたデータには難があることもわかったのでデータ取りや解釈のやり直し。

##デバイス特性
前回のデバイス特性のグラフはテスト時間を10秒ずつで測定していたため、書き込みの場合に途中でキャッシュが溢れて半端な値が計測されていた。そこでテスト時間を3秒にして、SLCキャッシュに納まる範囲でのデータを取り直す。

低レベルフォーマットをした後、READ 0→WRITE 0→READ 1→WRITE 1の順に3秒ずつ測定し、各測定の前には17秒の間隔を置いている。17秒というのは、SLCキャッシュの[書き込みへの影響](#書き込みへの影響)で計算した「TLC領域への書き出しは15秒くらいで終わるはず」という結果に基づき、トータルの実行時間を変えないよう調整したもの。

```shell:NVMe_perftest_3sec.sh
#!/bin/sh
for size in 512 1024 2048 4096 8192 16384 32768 65536 131072
do
  ./nvmecontrol format nvme0ns1
  sleep 17
  nvmecontrol perftest -n $1 -i bio -o read  -s $size -t 3 nvme0ns1
  sleep 17
  nvmecontrol perftest -n $1 -i bio -o write -s $size -t 3 nvme0ns1
  sleep 17
  nvmecontrol perftest -n $1 -i bio -o read  -s $size -t 3 nvme0ns1
  sleep 17
  nvmecontrol perftest -n $1 -i bio -o write -s $size -t 3 nvme0ns1
done
```

まず今回いままでの測定と同じ1スレッドでの計測結果。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253516/605cecfa-4e5e-6d0f-8956-4d23ca754272.png)![image.png](https://qiita-image-store.s3.amazonaws.com/0/253516/afa2beae-c49d-399a-9167-0e8009dc0fa7.png)

READ 0は前回は「未使用セクタの読み出し」と説明していたが、今回の考察より「SLCキャッシュからTLC領域への書き出しがない場合の読み出し」に相当すると考えられる。一方READ 1は「使用済みセクタの読み出し」ではなく、「SLCキャッシュからTLC領域への書き出しと競合した状態での読み出し」だろう。IOサイズ128KiBのときのREAD 1の測定値(530MB/s)が書き出しと干渉しているときのスループット(490MB/s)よりやや大きいので、書き出しが途中で始まったか途中で終わったか。書き込み直後に20秒待ってもSLCキャッシュに空きができないことを考えると、おそらく読み出し試験が始まったあとで（あるいはそれがきっかけで）TLC領域への書き出しが始まり、読み出し試験のあと17秒の間隔の間に完了したのではないか。

WRITE 0とWRITE 1は今回はほとんど同じ結果になった。前回のWRITE 0は10秒の測定時間中にSLCキャッシュが溢れて半端な数値になっていたが、今回のWRITE 0はほぼカタログ値で頭打ちになっている。一方前回のWRITE 1はSLCキャッシュが溢れた際の書き込み性能、今回のWRITE 1はWRITE 0と同じSLCキャッシュに対する書き込み性能となっている。前回測定では測定前の間隔が10秒しかないため、TLC領域への書き出しが終わっていなかった可能性が考えられる。今回はWRITE 1の測定前に17秒の間隔があり書き出しを十分に完了できるだけの余裕がある。

もっともこうした考察は、TLC領域への書き出し条件がわからない以上、憶測に過ぎない。

続いて16スレッドでの計測結果は前回とほとんど差が見られない。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/253516/9fc6cf8b-aa53-d2bb-0ca3-c8463f1b69bd.png)![image.png](https://qiita-image-store.s3.amazonaws.com/0/253516/eeba1097-a1d9-6744-251c-80014f3d228c.png)

マルチスレッドでアクセスすると、前回のようにSLCキャッシュが溢れている場合と、今回のように溢れていない場合とで、それほど大きな性能の差がないということになる。これは正直なところよくわからない結果である。

##ddによる性能測定
前回も1つの測定項目あたりのデータ量は1GとSLCキャッシュに納まる範囲だったので、測定結果自体はそのまま通用すると思われる。ブロックデバイスへの書き込み速度が速すぎると考えていたが、前回は`nvmecontrol perftest`の結果が過小になっていたので、結局それほど問題はなさそうである。ファイルシステムから読み出し速度が速いというのも、先読みなどの機構があればそれほど不思議な数値でもないように感じられる。