先日、FastEnum に関して非常に良い質問を受けました。短期間だけ起動してすぐにアプリ/インスタンスが死んでいくバッチ処理などにおいては、逆に初期化コストが大きくなってしまうのでないか?というものです。
はい、初回アクセス時に一度のみ初期化されることは分かるので、その初回アクセス時のコストは一回きりの直接使用よりはコストがかかると思います。仮に毎回コールドからだとしたら当然通常よりも遅くなるので、どの程度のキャッシュヒット率があれば採用した方が勝るかの計算が必要だと考えました。
— acple_ (@acple) October 27, 2019
確かに実行速度はメチャクチャ速いのですが、初期化コストがどの程度かは把握していませんでした。というのも、FastEnum は Web サービスやクライアントアプリなどの比較的長く利用されるものに対する応答速度を極限まで向上させることを目指しているので、初期化コストは warm up などで無視するものとして割り切っていました。ですが、確かに初期化コストを無視できないようなシチュエーションでの利用にどの程度耐えられるのかは気になります。
計測結果
ということで計測してみました。結果は以下のような感じです。呼び出し回数は、FastEnum の初期化の間に System.Enum
の各種メソッドを何度呼び出せるのかを示しています。
処理 | 時間 [ns] | 呼び出し回数 | アロケーション [B] |
---|---|---|---|
FastEnum.Init | 22,075.16 | 1.00 | 8,067 |
Enum.GetValues | 1,337.97 | 16.50 | 352 |
Enum.GetNames | 45.57 | 484.42 | 128 |
Enum.GetName | 58.82 | 375.30 | 24 |
Enum.IsDefined | 119.85 | 184.19 | 24 |
Enum.TryParse | 139.91 | 157.78 | 24 |
Enum.ToString | 33.95 | 650.23 | 24 |
どうやら、一度ひとつの列挙型を初期化するのは 17 回ほど Enum.GetValues()
を呼び出すのと同等なようです。Enum.ToString()
であれば 650 回ほど。こう見ると結構遅い...気がする!初期化するときの実装には言うほど気を配っていないのが明るみに出た感じががが...。
と言っても、これらの数字は Web サービスのリクエスト数などからすればすぐにペイできます。初期化コストを気にするよりも一度のリクエストを高速に捌く方がずっと大切なので、言うほどではないでしょう。逆に短命なアプリやバッチ処理の場合は利用しない方が良いでしょう。ケースバイケースで使い分けるのが良いかと思います。
けど、やっぱりもうちょっと初期化コストを抑えるように頑張っても良いかもしれない...