Skip to content

パフォーマンス

ウォームな解決は、1 回の Map.get(key) に続く直接的な new Ctor(...) です — リフレクションも、メタデータテーブルも、間に挟まるプロキシもありません。以下のベンチマーク数値は、オプトインで有効にする特別な高速モードからではなく、いくつかの具体的なランタイム上の選択から導かれたものです。

ランタイム上の選択効果
明示的な登録コンテナのビルドはサービスごとのフラットな Map.set です。準備すべきデコレーターの副作用も、コンストラクター名のパーサーも、メタデータテーブルもありません。
シングルトンおよびスコープドサービスのキャッシュウォームな解決は、サイクルとライフタイムの管理処理が実行される前に cache.get(key) から読み取ります。cache.has(key) のフォールバックは明示的な undefined 値のためだけに存在します。
直接的なコンストラクター呼び出し依存が 0〜7 個のクラスは直接的な new Ctor(...) の経路を使用します。より大きなコンストラクターは Reflect.construct にフォールバックします。
非同期ファクトリーファクトリーの Promise はそのままキャッシュされるため、.get() が同期のままで、並行する呼び出し元は進行中の初期化を 1 つ共有します。
strict モードの境界strict: true はサイクルとライフタイムのリークを検出します。strict: false は、監査済みのホットなトランジェントグラフに対してその管理処理を取り除きます。

ベンチマーク結果

ベンチマークスイート

リポジトリのベンチマークスイートは、InferDI を InversifyJS v8、PROXY および CLASSIC モードの Awilix v13、TSyringe v4、TypeDI v0.10、そして Typed Inject v5 と比較します。

すべての数値は Node 22 における 1 秒あたりの操作回数です。高いほど良好です。

シナリオInferDIInversifyJSTyped InjectAwilix (PROXY)Awilix (CLASSIC)TSyringeTypeDI
1. ホットなシングルトンの解決(ウォームキャッシュ)14.3 M10.7 M7.0 M7.3 M6.7 M5.8 M6.45 M
2. トランジェントの解決(呼び出しごとに新しいインスタンス)9.75 M6.1 M4.1 M3.45 M3.0 M2.5 M1.6 M
3. 深いグラフ(10 階層、すべてトランジェント)2.3 M1.5 M1.3 M716 k736 k643 k222 k
4a. 広いグラフ(4 依存、ルートがトランジェント)8.25 M4.9 M3.4 M2.2 M2.3 M1.65 M1.1 M
4b. 広いグラフ(10 依存、ルートがトランジェント)3.5 M1.9 M2.6 M1.2 M1.3 M938 k458 k
5. コンテナのビルド + 最初の解決400 k13.2 k223 k10 k8.3 k206 k282 k
6. スコープドのライフサイクル(作成 + 解決 + クリーンアップ)2.85 M35 k2.45 M330 k430 k1.1 M665 k
7. 遅延解決(遅延ラッパー)11.8 M7.6 M7.15 M5.6 M4.7 M4.25 M2.85 M

数値が示すもの

  • キャッシュされたシングルトンの解決は、最も近いベースラインである InversifyJS より 1.34 倍高速です。
  • コンテナのビルドと最初の解決はフラットな登録に有利です。InferDI はグラフをゼロから登録します。デコレーターベースのライブラリは、モジュールの評価中に登録作業の一部をすでに支払い済みです。
  • 広いグラフのシナリオは、アリティのアンローリングがなぜ重要かを示しています。4 個の依存では、InferDI は最も近いベースラインより 1.68 倍高速です。10 個の依存では Reflect.construct にフォールバックし、Typed Inject より 1.35 倍高速です。
  • スコープドのライフサイクルには、スコープの作成、解決、そしてクリーンアップが含まれます。シナリオ 6 は反復のたびに破棄処理を含むため、解決単独ではなくスコープの所有を測定します。
  • InferDI は全 8 シナリオで首位です。スコープドのフローと 10 個の依存を持つ広いグラフでは Typed Inject が最も近い非 InferDI のベースラインであり、キャッシュされたシングルトン、トランジェント、深いグラフ、4 個の依存を持つ広いグラフでは InversifyJS が最も近い位置にあります。

高速モード

new Container({ strict: false }) は、ランタイムのサイクル管理、シングルトンスタックの追跡、そしてガードされた解決経路を囲む try/finally を取り除きます。パッケージの README では、フラットなトランジェントグラフでローカルのトランジェント解決が約 30% 高速になると報告されています。キャッシュされたシングルトンおよびスコープドの解決は、それらのガードが実行される前に値を返すため変化しません。

高速モードは、デフォルトの strict モードでテストがグラフを十分に実行した後にのみ使用してください。TypeScript はシングルトンのサイクル、トランジェントのサイクル、動的キー、as キャスト、あるいはより広い外側のコンテナをクロージャに取り込むファクトリーを見ることができません。

プロファイリング済みで transient 解決が多い本番パスでは、その検証後の { strict: false } がサポート対象で最速の構成です。開発とテストでは strict モードを維持してください。キャッシュ済みの singleton または scoped 解決は速くなりません。

ホットパスの細かな詳細

シンボルキーは、Map がそれらを同一性で比較するため、タイトな解決ループで役立つことがあります。文字列キーはハッシュ化が必要で、衝突時には文字単位の比較が必要です。ほとんどのアプリケーションでは差を計測できないため、シンボルキーはプロファイラー主導の変更として扱ってください。

ローカルで再現する

bash
cd benchmarks
pnpm install --frozen-lockfile
pnpm run precondition
pnpm run bench

ベンチマークのワークスペースは意図的にルートの pnpm ワークスペースから分離されており、独自のロックファイルを持っています。方法論、公平性に関する注記、そしてフィクスチャのソースについては benchmarks/README.md を参照してください。