なんでbun installは速いのか?
⚡️ 25x faster — Switch from npm install to bun install in any Node.js project to make your installations up to 25x faster. https://bun.sh/docs/cli/install
という記述を見かけて直感的に、そうはならんやろと思ったものの実際にベンチマークをしているのでどういうことなのかを気になって調べた。
A global install cache.
bun installを実行すると ~/.bun/install/cache/
以下にnpmレジストリからダウンロードされたファイルの実体が展開されキャッシュされる(--cache-dir
でパスを変更できる)。
キャッシュにはパッケージのバージョンごとのディレクトリとlatestのシンボリックリンクがある。これらのファイルを使ってbun install時に該当するバージョンのダウンロードを省く(--force
で再ダウンロードする)。
このため一度bun installしてlockfileが作成されると次はオフラインでinstallを実行しても成功する。
node_modules/を削除しても~/.bun/install/cache/
からファイルを取ってくるのでネットワークアクセスをしない。
Optimized file writes.
node_modules/ 以下にファイルを配置する時に the fastest system calls を使うとある。
例えばmacOSではclonefileat()を呼び出すためにOSごとに分岐していた。
- https://github.com/oven-sh/bun/blob/80e1f32ca1236dcf6f7ed559c596afbced0f8f3a/src/install/install.zig#L1481
- https://github.com/oven-sh/bun/blob/80e1f32ca1236dcf6f7ed559c596afbced0f8f3a/src/install/install.zig#L1110
同じくキャッシュからnode_modules/ を生成するpnpmはシンプルにシンボリックリンクを張って回っているので実装に違いがあるらしい。
bun installで生成されたnode_module以下のファイルをstatコマンドで見てみたところリンクファイルじゃなくて実体だった。
A binary lockfile.
速さとは直接関係なさそうだけど、bunのlockfileはバイナリで吐き出される。
これはbun-lockfile-format-v0という形式で、bunコマンドで実行するとyarnv1形式のプレーンテキストを吐いてくれる。
❯ bun bun.lockb | head
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
# bun ./bun.lockb --hash: 5D27A8E737B929B1-08d055213fe5158d-223CA8C1A1ADEB6B-1c1460a293fede0f
"@aashutoshrathi/word-wrap@^1.2.3":
version "1.2.6"
resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz"
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
bun installに限らずbunはデコードするとJavaScriptになる中間形式を各所で吐き出して高速化しているので、インストール時にも何か関係があるのかもしれない。
pnpmとの比較の話
Evan Youがベンチマークを同条件にして比較している。それによると
- キャッシュの有無でのパッケージダウンロードが発生するか
- lockfileを生成するためのNPM APIのアクセス
が重要らしい。
筆者環境でもbunとpnpmでキャッシュとlockfileを用意して比較すると差は5-6倍程度だった。これなら妥当な感じ。
とすると後はダウンローダーとパッケージ展開のプログラムの処理スピードで差が出ているなと予想した。
Zig実装が速い
インストールコマンドの実装は src/install/
あたりにある。
HTTPClientもmulti-threaded処理も含んだzig実装なので単にシングルスレッドなNode.jsスクリプトで実行するよりは速くなりそうだというbun devでNext.jsが実行される仕組みを調べるで調べた時と同じ感想になった。
もう少し解像度の高い解説はZigつよつよな人にお願いしたい。