Diary of a Perpetual Student

Perpetual Student: A person who remains at university far beyond the normal period

パフォーマンスだけを考えると WMI を使わないほうが良いのかもしれない

OS 名は WMI から取得したほうが良いが……

前にこんなエントリを書いた。

blog.arthur1.dev

このエントリを要約すると、Windows の OS 名を取得するとき、レジストリにアクセスするのではなく WMI を使って取得しよう。なぜなら、Win 11 が Win 10 として認識されてしまうから」という内容である。

mackerelio/mackerel-agent には、WMI を利用せずに基盤の情報を取得している箇所がいくつかある。たとえば、レジストリの値を参照していたり、dll を読んで Windows API を呼び出したりしている。こういった場所も WMI に置き換えたほうが良いのだろうか。

それは、現状持ち合わせている情報では分からない、というのが誠実な答えになるだろう。OS 名に関してレジストリの値が信用ならないことは分かったが、それが一般に成り立つとは限らない。

Windows の細かい話は素人にはよく分からないので総合的な良し悪しはあまり語れないが、今回はわかりやすいパフォーマンス面での比較をしようと思う。

パフォーマンス比較実験

実験のために書いたプログラムは以下のリポジトリで公開している。

github.com

実験設定

比較対象は3つの手法である:

  1. dll を読み込み Windows API を呼び出す手法
  2. WMI に WQL クエリを投げる手法
  3. レジストリの値を読み出す手法

この3つの手法それぞれで同等な情報を得られるものを探した結果、プロセッサのアーキテクチャを取得させることにした。

各手法の実装は、mackerelio/mackerel-agent で利用している util パッケージを import する、もしくは同等のコードを書くことによって行った。

ベンチマークには Go 言語に標準で付随する testing パッケージの Benchmark を利用する。

比較対象は、自分所有の PC *1において以下のコマンドで benchmark を実行したときに表示される ns/op (関数1実行あたりにかかったナノ秒)である。

go test .\windows\ -bench .

実験結果

上記コマンドを実行すると、以下のような出力が得られた。

> go test .\windows\ -bench .
goos: windows
goarch: amd64
pkg: github.com/Arthur1/windows-api-benchmark/windows
cpu: 12th Gen Intel(R) Core(TM) i7-12700F
BenchmarkGetProcessorArchitectureFromApi-20     1000000000
--- BENCH: BenchmarkGetProcessorArchitectureFromApi-20
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
BenchmarkGetProcessorArchitectureFromWmi-20     1000000000               0.01329 ns/op
--- BENCH: BenchmarkGetProcessorArchitectureFromWmi-20
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
BenchmarkGetProcessorArchitectureFromReg-20     1000000000
--- BENCH: BenchmarkGetProcessorArchitectureFromReg-20
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
PASS
ok      github.com/Arthur1/windows-api-benchmark/windows        0.782s

この結果を見ると、Windows APIレジストリを用いる手法では ns/op が表示されないほど短い時間で実行できているが、WMI を用いる手法だと1実行あたり 0.013 ns ほど掛かっているということが分かる。

どこで詰まっているか(詰まっているというほどではないと思うが)気になったので、pprof を使って詳しい様子を観察してみた。概ね、WMI にクエリを投げるのに相当するシステムコールに時間が掛かっている、といったところだろうか。

まとめ

今の mackerel-agent の実装をすべて WMI を使う形に置き換えればいいじゃんと思っていたのだが、パフォーマンスを比較すると WMI が劣っているということを知った。劣っているといっても 0.1 ns にも満たない程度なので、(クエリにもよるだろうが)通常の利用の範囲では問題ないように感じる。

つまり、安定して情報が取得できるか、得た情報が正しいかどうか、などの他の軸に重きをおいて手法を評価したほうが良いということになる。冒頭に紹介したエントリでは得た情報の正しさを鑑みて WMI を選択したが、それに関しては良い選択だったのではないかと思う。

Windows に詳しい人はぜひいろいろ教えてほしい。

*1:Z690 / Core i7-12700F / RTX3080 / DDR5-4800 32GB の自作 PC。OS は Windows 11 Pro