Node.js 22にmodule.enableCompileCache()
というAPIが導入されたらしく、これを利用したTypeScriptコンパイラーでは2.3倍、ESLintでは1.3倍の速度改善がなされたと聞いて気になって調べてみました。
module.enableCompileCache()とは?
Node.jsのモジュール(JavaScriptファイル)をコンパイルした結果をディスク上にキャッシュし、2回目以降の実行を高速化する機能です。
import { enableCompileCache } from 'node:module';
// キャッシュを有効化
enableCompileCache();
// この後に読み込まれるモジュールは全てキャッシュの対象に
import './other.js';
仕組み:V8のコード・キャッシング
Node.jsがJavaScriptファイルを実行する際の流れは通常、
ソースコード → パース → バイトコードにコンパイル → 実行
のようになるのですが、キャッシュを使った場合、
1回目:
ソースコード → パース → バイトコードにコンパイル → 実行
2回目以降:
バイトコードのキャッシュを読み込む → 実行
となり、パースとコンパイルの処理を省略できるため高速化されるわけです。
これを裏で支えているのがV8のコード・キャッシングです:
実際の効果
Node.jsのプルリクエストには実際の効果について報告されていました。
TypeScriptコンパイラーに導入したときの効果:
- キャッシュなし: Time: 120.5 ms ± 1.7 ms
- キャッシュあり: Time: 51.8 ms ± 1.0 ms
→ 2.33倍の高速化
ESLintに導入したときの効果:
- キャッシュなし: Time: 129.9 ms ± 7.8 ms
- キャッシュあり: Time: 105.5 ms ± 1.8 ms
→ 1.23倍の高速化
具体的な使い方
// エントリーポイント(例:bin/cli.js)で1回だけ呼び出し
import { enableCompileCache } from 'node:module';
// 環境変数でキャッシュが無効化されていない場合のみ有効化
if (process.env.NODE_DISABLE_COMPILE_CACHE !== '1') {
enableCompileCache();
}
// 以降の全てのモジュールがキャッシュ対象に
import { main } from '../src/index.js';
main();
キャッシュの場所
デフォルトではos.tmpdir()/node-compile-cache
に保存されます。変更したい場合は:
- 環境変数で指定:
export NODE_COMPILE_CACHE=/path/to/cache
- API呼び出し時に指定:
enableCompileCache('/path/to/cache');
キャッシュの無効化
export NODE_DISABLE_COMPILE_CACHE=1
効果が高いケース・低いケース
効果が高い:
- CLIツール(TypeScript, ESLint等)
- 毎回
node
コマンドで起動するため、2回目以降が高速化
- 毎回
- 同じコードを何度も実行する場合
- 大きなライブラリを使用する場合
効果が低い:
- Webサーバーのように常駐するプロセス
- 1回起動したら動き続けるため、キャッシュの恩恵が少ない
- コードが頻繁に変更される場合
- 実行時に動的に生成されるコード
注意点
- Node.jsのバージョン間でキャッシュは共有されません
- テストカバレッジを取る際は、精度が低下する可能性があるため無効化を推奨
- ワーカースレッドで使用する場合は、各ワーカーで個別に有効化するか環境変数を設定する必要があります
Dockerでの利用
バイトコードキャッシュをコンテナビルド時に作っておけば、Webサーバーにも応用できるかもしれません。
単純なDockerでの再利用は以下の理由で難しいですが:
- キャッシュはNode.jsのバージョンに紐付く
- デフォルトでは一時ディレクトリに保存
- キャッシュ内のパスが絶対パス
以下のような工夫で、もしかしたら事前にキャッシュを用意できるかもしれません:
# キャッシュを事前生成するステージ
FROM node:22 AS builder
COPY . .
RUN NODE_COMPILE_CACHE=/app/cache node your-script.js
# 実行用ステージ
FROM node:22
COPY --from=builder /app/cache /app/cache
ENV NODE_COMPILE_CACHE=/app/cache
ここは全くの未検証ですが、もしこれが可能なら、Kubernetesのようにコンテナーを自動スケールアウトするような環境や、たまにしかアクセスのないアプリをscale-to-zeroしつつ、瞬時に起動するといったことに繋がりそうで夢があります。
まとめ
module.enableCompileCache()
は、特にCLIツールなど、Node.jsを頻繁に起動するアプリケーションで大きな効果を発揮します。エントリーポイントで1行追加するだけで有効になる手軽さと、最大2.3倍という高速化効果は、検討する価値が十分にあると言えるでしょう。