Windowsプロセス間通信の速度比較
Windows環境でのプロセス間通信では、一体どういった構築がベストなのか、資料を見つけられなかったので適当に測定してみました。
こういうのは環境によるところが多いので、常に使えるデータではありませんが、一つの参考にはなるかもしれません。
測定環境:Windows10 / Rysen5-5600X / SSD2TB / メモリ64GB
※ Debugビルドしかしていないし、全くもって厳密な測定ではありません…
調べたい項目
- プロセスの起動速度
- 通信手法
- 転送バッファサイズと転送速度の関係
プロセスを新規に起動してそれに対してプロセス間通信を行う、という処理一式の軽量さを判断したいので、プロセス自体の起動速度も比較対象に含めました。
より詳細な比較項目
- SubSystem:Console vs SubSystem:Windows
- 匿名パイプ vs 共有メモリ(=メモリマップトファイル)
- 小さいバッファサイズで細切れに転送 vs 大きいバッファサイズで一度に転送
通信手法については、名前付きパイプ・ソケット通信・ファイルで通信 などは速度面から不利であることが分かっているので、検証対象から外しました。
一般的には共有メモリがダントツで早いと言われていますが、Windowsのそれはメモリマップトファイルであり、メモリにキャッシュはされるもののストレージのアクセス速度が足を引っ張る可能性があります。
測定方法
- 子プロセスを起動
- 匿名パイプで、親から子にデータ要求を出す
- 匿名パイプ or 共有メモリ で、子から親にBlockSize分のデータを渡す
- 親が累計TotalSize分のデータを受け取ったら終了。まだなら2に戻る
以上を SubSystem:Console の場合と SubSystem:Windows の場合で行う。
結果
プロセス起動処理にかかる時間は
Console 匿名パイプ | Windows 匿名パイプ | Console 共有メモリ | Windows 共有メモリ | |
---|---|---|---|---|
一度目 | 17ms | 2ms | 17ms | 2ms |
以降 | 2ms | 2ms | 2ms | 2ms |
転送処理にかかる時間は、TotalSize:1GB のとき
Console 匿名パイプ | Windows 匿名パイプ | Console 共有メモリ | Windows 共有メモリ | |
---|---|---|---|---|
BlockSize:32KiB | 430ms | 500ms | 500ms | 440ms |
BlockSize:64KiB | 250ms | 290ms | 370ms | 315ms |
BlockSize:128KiB | 150ms | 175ms | 245ms | 230ms |
BlockSize:256KiB | ★ 130ms | 130ms | 130ms | 135ms |
BlockSize:512KiB | 200ms | 200ms | ☆ 115ms | 130ms |
BlockSize:1MiB | 410ms | 430ms | 485ms | 490ms |
BlockSize:4MiB | 450ms | 565ms | 470ms | 480ms |
BlockSize:8MiB | 620ms | 690ms | 515ms | 505ms |
BlockSize:16MiB | 760ms | 820ms | 715ms | 700ms |
BlockSize:32MiB | 1045ms | 1090ms | 950ms | 930ms |
考察
プロセス起動にかかる時間について
SubSystem:Consoleの場合はコンソールウィンドウに標準出力をアタッチしたりなんなりとかのコストがかかるでしょうから、 SubSystem:Windows の方が早いのは理解できます。連続して何度も呼んだ場合は何かしらのキャッシュに乗るのか、WindowsでもConsoleでも同じ2msになりました。
転送処理にかかる時間について
Subsystemは関係ないだろうと思っていたのですが、おおむね(特に匿名パイプは)Consoleの方が早いという結果が出ました。原因は分かりません。(誤差かもしれません)
共有メモリは僅かに早いケースもあるものの、匿名パイプとほぼ変わらないという結果になりました。
もしかしたら読み込みの同期に匿名パイプを併用する必要がある以上、その同期に時間が掛かってしまって速さを発揮できていないのかもしれません。ただ同期は絶対に必要なので、やはり結果的にはWindowsの共有メモリによる通信は匿名パイプとほぼ変わらないと言えるかもしれません。
少なくとも「共有メモリがダントツで早い」というイメージからはほど遠いですね…。
総評としては
Subsystem:Console の方がスタンドアロンでのテストが行いやすいというメリットがあるので、同じイメージファイルで何度もプロセスを立ち上げるのであればほぼ速度は変わらないですし、メンテナンス性の観点からConsoleにした方がよさそうです。
また共有メモリは途中で例外が起きたりしてうっかり開放をしそびれると、再起動まで永久に無駄ファイル&メモリが残り続ける恐れがあります。
そういう扱いが危険な代物でもあるので、早さの違いがほぼないのであれば選択するメリットはないでしょう。
使用すべきブロックサイズは環境にもよるでしょうが、とりあえず表から見て256KiBあたりが良いであろうことは分かります。
結論
今回のケースでは
- Subsystem:Console
- BlockSize:256Kib
- 通信方式:匿名パイプ
とするのが良いかなという結論が得られました。