Windowsで検証したのでタグはWindowsとしていますが、本記事で重点的に検証したdiskspd自体はLinux対応していて、Linuxでもオプション指定等の検証内容は有効だと思います。diskspdは高機能のため、今後機会があればLinuxでもdiskspdを利用したいです。
オンプレWindowsサーバのディスク自体のパフォーマンスを計測したい&継続的に色々な環境のデータを集めてディスクパフォーマンス周りのナレッジ(どのくらいのパフォーマンスの環境が多いのかとか、パフォーマンス問題が起きる環境のIOPSはこのぐらいが多いとか色々)を貯めたいなあ..と思い、パフォーマンス計測ツール/ベンチマークソフトを調べた。
ディスクのパフォーマンス測定というと真っ先にCrystalDiskMarkが浮かぶんだけど、フリーソフトは今回は却下。となるとどういう手法があるんだ?と調べてみると、ビルトインのwinsatというコマンドがあって、一応それで測れる(ただし、後から分かったけど、クライアントOSのみ対応で、サーバOSでは利用不可だった)。正式名称はWindows システム評価ツール(Windows System Assessment Tool)で、Windows エクスペリエンス(CPU性能評価とかするあれ)に関するやつみたい
winsat
何も考えずにwinsat disk
と実行するだけで、シーケンシャルリード、シーケンシャルライト、ランダムリードをパッと測ってくれる。ランダムライトは測ってくれないので、オプションを付けて別途実行するwinsat disk -ran -write -n 0
。 -ran
はランダム、-write
はライト、-n 0
は1つめのディスクを指定している
実行結果はこんな感じ
C:\>winsat disk
Windows システム評価ツール
> 実行中: 機能の列挙 ''
> 実行時間 00:00:00.00
> 実行中: 記憶域の評価 '-seq -read -n 0'
> 実行時間 00:00:06.74
> 実行中: 記憶域の評価 '-ran -read -n 0'
> 実行時間 00:00:11.12
> 実行中: 記憶域の評価 '-scen 2009 -drive C:'
> 実行時間 00:01:09.03
> 実行中: 記憶域の評価 '-seq -write -drive C:'
> 実行時間 00:00:09.20
> 実行中: 記憶域の評価 '-flush -drive C: -seq'
> 実行時間 00:00:05.55
> 実行中: 記憶域の評価 '-flush -drive C: -ran'
> 実行時間 00:00:12.82
> 実行中: 記憶域の評価 '-hybrid -ran -read -n 0 -ransize 4096'
NV Cache not present.
> 実行時間 00:00:00.16
> 実行中: 記憶域の評価 '-hybrid -ran -read -n 0 -ransize 16384'
NV Cache not present.
> 実行時間 00:00:00.02
> Disk Sequential 64.0 Read 117.78 MB/s 6.9
> Disk Random 16.0 Read 1.45 MB/s 3.9
> Responsiveness: Average IO Rate 3.36 ms/IO 6.2
> Responsiveness: Grouped IOs 10.28 units 7.1
> Responsiveness: Long IOs 15.11 units 6.9
> Responsiveness: Overall 155.25 units 6.6
> Responsiveness: PenaltyFactor 0.0
> Disk Sequential 64.0 Write 74.90 MB/s 6.1
> シーケンシャル書き込みでの平均読み取り時間 5.162 ms 5.
> 待ち時間: 95 パーセンタイル 40.187 ms 1.9
> 待ち時間: 最大 72.808 ms 7.8
> ランダム書き込みでの平均読み取り時間 11.903 ms 4.0
> 合計実行時時間 00:01:55.43
C:\>winsat disk -ran -write -n 0
Windows システム評価ツール
> 実行中: 機能の列挙 ''
> 実行時間 00:00:00.00
> 実行中: 記憶域の評価 '-ran -write -n 0'
> 実行時間 00:00:01.68
> Disk Random 16.0 Write 13.15 MB/s
> 合計実行時時間 00:00:02.39
で、だらだらと出力されていてちょっと読みづらいが、要するにここを見る
> Disk Sequential 64.0 Read 118.46 MB/s 6.9
> Disk Random 16.0 Read 1.49 MB/s 3.9
> Disk Sequential 64.0 Write 74.19 MB/s 6.1
> Disk Random 16.0 Write 12.91 MB/s
ブロックサイズ64KBでのシーケンシャルリードは118.46MB/s、16KBでのランダムリードは1.49MB/sという感じ。
Windowsのアロケーションユニットサイズの標準は4KBなので、ブロックサイズでも4KBを指定し、ランダムアクセスのみ測る。その場合はこんな感じ
winsat disk -ran -ransize 4096 -n 0 -read
winsat disk -ran -ransize 4096 -n 0 -write
実行時間は短く(基本数秒。ただしディスクが遅いサーバでは数分かかることがある)、結果は安定していて、複数回測ってもほとんど同じ値なのでキャッシュうんぬんは気にせず1-2回測ればOKそうな感じ。
ただ、環境によって、ランダムライトがランダムリードの10倍くらい速いと記録されることがあって、嘘..?と(基本ライトの方が遅いと思うので)。
なお今回試したのはwinsat disk
だけだけど、winsatツール自体はコンピュータの色々なリソースに対する包括的なパフォーマンス計測ツールのようで、winsat cpu
winsat mem
など色々測れる
で、パフォーマンスツールによって違う結果が出るんだろうな..というのは考慮としてあって、社内環境にCrystalDiskMark入れて測ってみる。やはり全然結果が違う..
CrystalDiskMark
winsat実行と同じマシンでのCrystalDiskMark
ランダムアクセスのスレッド数をどちらも1とし、テスト回数を1回とした(回数を変えて繰り返し測ってもあまり変わらなかったため)。それ以外はデフォルト
繰り返し実行したりタイミングを変えて実行するとたまに結果が違うので微妙なんだけど(共有環境なのもあり)、意外とキュー数などによって、ライトが速いケースもあるみたい。ただし、数値はwinsatよりだいぶ低い..
で、計測ツールについてさらに調べると、Microsoft謹製のdiskspdというパフォーマンス計測ツールがあり(しかもLinuxも対応)、実はCrystalDiskMarkはdiskspdをラッパーしてるということだった
ベンチマーク機能
CrystalDiskMark 4-8 は Microsoft DiskSpd (The MIT License) を使用しております。
Microsoft謹製かつ、コマンドラインツールなことが丁度良いので(結果をリダイレクトでテキストに落とせて楽)、diskspdを重点的に検証してみた
diskspd
GitHubからzipをダウンロードして解凍、コマンドプロンプトでDiskSpdフォルダにcdしてdiskspd.exe
を実行する(インストール不要 ※レジストリ汚れない)
diskspdはオプションが豊富で、リードとライトの割合や、キャッシュの有効/無効を指定できたりする。CrystalDiskMarkのラッパー元なので、CrystalDiskMarkにある設定項目も指定できる。出力結果も、スループット/IOPSに加え、レイテンシーのパーセンタイルや標準偏差など色々出せる。
試行錯誤の結果、実行時オプションは以下のようにしてる。
ランダムリード
diskspd.exe -b4K -c1G -r -o1 -t1 -Sh -n -D -L -W0 -d10 -w0 testfile.dat
diskspd.exe -b4K -c1G -r -o32 -t1 -Sh -n -D -L -W0 -d10 -w0 testfile.dat
ランダムライト
diskspd.exe -b4K -c1G -r -o1 -t1 -Sh -n -D -L -W0 -d10 -w100 testfile.dat
diskspd.exe -b4K -c1G -r -o32 -t1 -Sh -n -D -L -W0 -d10 -w100 testfile.dat
各オプションの説明はこんな感じ
-b4K ブロックサイズ4KB(Windows標準のアロケーションユニットサイズを指定)
-c1G テスト用ファイル(testfile.dat)のサイズ。ファイルは初回実行時に自動生成される。フォルダ名指定しない場合はカレントディレクトリに生成される。
-r ランダムリード/ライト指定(シーケンシャルリード/ライトにしたい場合は-s)。-r4Kとブロックサイズまで指定しても良いかもしれない。英語圏のサイトの実行例では-r指定が多かったのと、実行時出力上でも-b4Kと-rの組み合わせでusing random I/O (alignment: 4096)と出力されているので-rで良いと判断
-o1[-o32] キューデプス。CrystalDiskMarkのキューと同じ。ネイティブコマンドキューイング(NCQ)対応のディスクの場合シークタイムの効率化が実施されIOPSが高くなる。未対応(1)、NCQ最大値対応(32)の両方のケースの測定ができるように、このオプションを分けてリード/ライトを各2回実施している
-t1 同時スレッド数。今回はシリアルアクセスのワークロードを想定しているので1を指定(同時アクセスの想定でここの数値を変える)。ちなみにスレッド数が2以上の場合、各スレッドでの結果はかなり低く、スレッド合計ではかなり高くなる。スレッド数の指定と評価は結構難しい感じがする
-Sh ソフトウェア/ハードウェア的なキャッシュ両方を無効 (-hオプションはduplicate)
-n CPUアフィニティを無効(CrystalDiskMarkのデフォルトが無効だったので合わせた。ちなみに有効(diskspdデフォ)でも無効でも結果に影響はなかった。CPUの各コアの使い方まで細かく考慮する必要は今回は感じてない)
-D IopsStdDev(IOPSの標準偏差)が追加で出力される。いらないけど一応
-L レイテンシー統計を出力
-W0 実行時間のうち、最初の方の計測結果を無視したいときは秒数を指定。今回は検証後最終的に実行時間を10秒と長めに取っていて、最初の方の結果を捨てても捨てなくても結果に大きな違いはないので、0を指定。ただし計測時間が数秒と短いとか、他のオプション指定との組み合わせによっては、最初の1~数秒間の結果がかなり良くなるケースがある。そういう場合は最初の部分を捨てるといいと思う。
-d10 実行時間を指定。10秒はデフォルトと同じ。実際1秒~20秒の各秒数で繰り返し計測したが、10秒くらいが結果が安定してるなという感じだった。1秒でも安定してるときは安定してるんだけど念のため長めに。
-w0[-w100] ライトの割合。-w0を指定するとリードオンリー。-w100を指定するとライトオンリー。リード/ライト両方を想定したシナリオの場合、ここで割合を指定できる
testfile.dat テストファイル名。このファイルが-cで指定したサイズで作成される
ちなみに他にも有用なオプションはある。この辺
-Zr テスト用データ(テスト用ファイルに書き込むバイト文字列)をランダムな値にする。デフォルトはzero埋め。ランダムな値の方が良い感はある。繰り返し試してみたけど結果に影響しなかったので指定してない。-z/-Z系はテスト用データに関するオプション
-v verbose。詳細や進捗を出力する。進捗は、最初はあるとなんかいいかもしれない。慣れるといらない。追加の情報で特に必要そうなものはなかったので指定してない
なお、一番影響がでかい要素は、意外と-c1G
部分のテスト用ファイルサイズ指定だった。CrystalDiskMarkではデフォルトで1GBとなっている。diskspdの計測結果は、実行時間ベースであって、XXバイトのリード(orライト)が完了するといった計測方法はないので、テスト用ファイルサイズの影響はあまりないかなと思っていたのだけど、最初適当に-c10M
や-c100M
などで検証していると、CrystalDiskMarkと比べて結果が良すぎる感じだった。影響のありそうなオプションをあれこれ試してると、-cXX
の部分の影響がとても大きいことに気づいた。CrystalDiskMarkと同様の1GBを指定したところ、CrystalDiskMarkに近めの値を出すことができたので、理解できてないけど1GB設定にすることにした。書き込むバイト文字列をランダムにする-Zr
などは、まったく影響がなかった。
上記のコマンドを実行すると以下のような結果となる
C:\>diskspd.exe -b4K -c1G -r -o1 -t1 -Sh -n -L -D -W0 -d10 -w0 testfile.dat
Command Line: diskspd.exe -b4K -c1G -r -o1 -t1 -Sh -n -L -D -W0 -d10 -w0 testfile.dat
Input parameters:
timespan: 1
-------------
duration: 10s
warm up time: 0s
cool down time: 0s
affinity disabled
measuring latency
gathering IOPS at intervals of 1000ms
random seed: 0
path: 'testfile.dat'
think time: 0ms
burst size: 0
software cache disabled
hardware write cache disabled, writethrough on
performing read test
block size: 4096
using random I/O (alignment: 4096)
number of outstanding I/O operations: 1
thread stride size: 0
threads per file: 1
IO priority: normal
System information:
computer name: xxxxxxxx
start time: 2022/03/30 05:58:02 UTC
Results for timespan 1:
*******************************************************************************
actual test time: 10.00s
thread count: 1
proc count: 4
CPU | Usage | User | Kernel | Idle
-------------------------------------------
0| 2.18%| 1.25%| 0.94%| 97.82%
1| 2.96%| 0.00%| 2.96%| 97.04%
2| 2.18%| 1.09%| 1.09%| 97.82%
3| 0.00%| 0.00%| 0.00%| 100.00%
-------------------------------------------
avg.| 1.83%| 0.59%| 1.25%| 98.17%
Total IO
thread | bytes | I/Os | MiB/s | I/O per s | AvgLat | IopsStdDev | LatStdDev | file
------------------------------------------------------------------------------------------------------------------
0 | 6275072 | 1532 | 0.60 | 153.20 | 6.522 | 4.17 | 2.518 | testfile.dat (1024MiB)
------------------------------------------------------------------------------------------------------------------
total: 6275072 | 1532 | 0.60 | 153.20 | 6.522 | 4.17 | 2.518
Read IO
thread | bytes | I/Os | MiB/s | I/O per s | AvgLat | IopsStdDev | LatStdDev | file
------------------------------------------------------------------------------------------------------------------
0 | 6275072 | 1532 | 0.60 | 153.20 | 6.522 | 4.17 | 2.518 | testfile.dat (1024MiB)
------------------------------------------------------------------------------------------------------------------
total: 6275072 | 1532 | 0.60 | 153.20 | 6.522 | 4.17 | 2.518
Write IO
thread | bytes | I/Os | MiB/s | I/O per s | AvgLat | IopsStdDev | LatStdDev | file
------------------------------------------------------------------------------------------------------------------
0 | 0 | 0 | 0.00 | 0.00 | 0.000 | 0.00 | N/A | testfile.dat (1024MiB)
------------------------------------------------------------------------------------------------------------------
total: 0 | 0 | 0.00 | 0.00 | 0.000 | 0.00 | N/A
total:
%-ile | Read (ms) | Write (ms) | Total (ms)
----------------------------------------------
min | 0.393 | N/A | 0.393
25th | 4.483 | N/A | 4.483
50th | 6.567 | N/A | 6.567
75th | 8.652 | N/A | 8.652
90th | 9.882 | N/A | 9.882
95th | 10.313 | N/A | 10.313
99th | 10.880 | N/A | 10.880
3-nines | 13.932 | N/A | 13.932
4-nines | 22.049 | N/A | 22.049
5-nines | 22.049 | N/A | 22.049
6-nines | 22.049 | N/A | 22.049
7-nines | 22.049 | N/A | 22.049
8-nines | 22.049 | N/A | 22.049
9-nines | 22.049 | N/A | 22.049
max | 22.049 | N/A | 22.049
各ツールでの計測結果
各ツールでの計測結果をまとめると、以下のようになる
共通設定部分は、ブロックサイズ4KBのランダムアクセス
スループット(MB/s) | winsat | CrystalDiskMark | diskspd |
---|---|---|---|
ランダムリード | 0.37(Q?) | 0.39(Q1), 0.95(Q32) | 0.60(Q1), 1.68(Q32) |
ランダムライト | 6.01(Q?) | 0.79(Q1), 0.84(Q32) | 1.47(Q1), 1.62(Q32) |
IOPS | winsat | CrystalDiskMark | diskspd |
---|---|---|---|
ランダムリード | 測定不可 | 95(Q1), 232(Q32) | 153(Q1), 429(Q32) |
ランダムライト | 測定不可 | 191(Q1), 205(Q32) | 375(Q1), 413(Q32) |
ツールによって値が異なるのと、実行するタイミングによっても値が上下するので、繰り返しの測定は必要(自分が完全に掌握してるマシンで無風状態と分かってるならまだいいんだけど共有環境は特に注意)。
また、計測にあたって、検証環境は低スペックなマシンで行ったが、一方、SSDを積んだマシンでも同様の傾向か確認する必要がある。
などなど前提や注意事項は色々付随してくるんだけど、今回のように各ツールの特性を調査してある程度傾向が分かってくれば、あとはどんどん実施して、ナレッジ蓄積と測定手法の改善サイクルを回していけるといいなと思う
diskspdを正しく理解するための参考リンク
MSの紹介記事読む。「キー パラメーターを指定する」の項で主要な幾つかのオプションについて分かりやすく書かれている
https://docs.microsoft.com/ja-jp/azure-stack/hci/manage/diskspd-overview
diskspdのコードリポジトリ。Code
Download Zip
からダウンロードする。
https://github.com/microsoft/diskspd
コマンドラインリファレンス
https://github.com/Microsoft/diskspd/wiki/Command-line-and-parameters
各オプションを検討するときに読む
https://github.com/Microsoft/diskspd/wiki/Customizing-tests
キューデプスの指定について
キューデプス(-o1/-o32
)の指定で結構パフォーマンス変わるけど、現在のサーバならNCQ対応してること前提で-o32
決め打ちで良いのでは..?感はある。また、HDDならではのシークタイム/回転待ち時間の効率化ならSSDでは気にしなくて良いのではと思うがSSDでもパフォーマンスに貢献してるということでここの指定はとても微妙。悩むなら両方指定して測っておくか、という感じ。
CrystalDiskMarkとdiskspdで結果が違う点について
CrystalDiskMarkはdiskspdをラッパーしてるのだから、オプション指定などが一緒なら、どちらも同じ結果がでるはずと思ってた(結果が違うなら、オプション指定などで差があるのでは)。しかし、実際にはCrystalDiskMarkの方が数値が低い。オプションの指定に差異があるのではと、CrystalDiskMarkの設定と、diskspdのオプションを見比べて色々試してみるんだけど、結局同じ値を出すことはできなかった。CrystalDiskMarkの設定を意識したオプションとしては、-c1G
-o1[-o32]
あたり。上にも書いたが、特に-c1G
の指定が影響が大きい。ここは判然としないのだが各ツールの特性として覚えておこうという感じ。
余談
diskspdの使用例について海外のサイトを調べているときに(どのサイトだったか忘れてしまったのですが)、一つのディスクにCとDドライブが構成されているとして、机上ではCドライブの方が高速となることを考慮する、と書いてあるのを見てなるほどーとなりました。ディスクは外周の方が高速で、外周から利用されるので、ディスクの前の方にあるCドライブの方が高速になる、と。ただ、ディスクをフォーマットするときにクイックフォーマットする場合は、これは当てはまらないんだろうなあと思います。完全フォーマット(全セクタ0埋め)する場合は、各セクタがどのドライブ用かはあらかじめ割り当てられていることになるので、外周に配置されるCドライブ内のファイルの方が速そうです。CrystalDiskMarkでフォルダを指定する設定があるのはその辺が理由かなと思います。ただしこれは共有ストレージやクラウドを使ってる場合は、ストレージ層は抽象化されていて、OSから見たセクターの位置と実際のストレージのディスク上のそれは一致していないことが多いと思うので、ディスクの外周とか気にするのはナンセンスになりそうです。