- Bunのパッケージインストールは、既存のパッケージマネージャーと比べて非常に高速に動作する
- 高速インストールの鍵は、システムプログラミングの観点からのアプローチとシステムコールの最小化にある
- Zig言語ベースのネイティブ実装、バイナリキャッシュの利用、OSごとの最適化など、細かな戦略の適用によって性能を向上させている
- tarballの展開とファイルコピーの過程でも、ハードウェア特性を活かした高性能な手法を導入している
- 依存関係グラフやlockfileなど、データ構造の最適化によってCPUキャッシュ効率とメモリアクセス性を高めている
Bun Installが速い理由
- Bunの
bun installは平均して、npmより7倍、pnpmより4倍、yarnより17倍高速なパッケージインストール性能を提供する
- これは単なるベンチマークではなく、パッケージインストールの問題をJavaScriptではなくシステムプログラミングの観点から捉えた結果である
- システムコールの最小化、マニフェストのバイナリキャッシュ、tarball展開の最適化、OSネイティブなファイルコピーなど、複数の層で積極的に性能最適化を適用している
Node.jsとパッケージマネージャーアーキテクチャの限界
- 2009年のNode.js登場以降、イベントループとスレッドプールベースの非同期IOモデルがパッケージマネージャーにも広がった
- 当時はハードウェアの制約(遅いディスク、遅いネットワーク)があったため、非同期IOと高頻度のシステムコールという戦略は合理的だった
- しかし現代のシステムでは、NVMe SSD、高速ネットワーク、高性能CPUが一般的であり、本当のボトルネックはIOではなくシステムコールのオーバーヘッドである
システムコールとモードスイッチのコスト
- プログラムがファイル読み込みのような処理を要求すると、user modeからkernel modeへの切り替えが必要になり、この過程で高コストなCPUサイクル(1000〜1500 cycles)が消費される
- パッケージインストールは基本的に数万回から数十万回以上のシステムコールを必要とするため、実作業そのものではなく切り替えコストだけで数秒分のCPU時間を消費することがある
- たとえばReactとその依存関係をインストールする際、npmは約100万回、yarnは400万回、pnpmは50万回、bunは16万回のシステムコールを使う
既存のパッケージマネージャーとBunのアプローチの違い
- npm、pnpm、yarnはいずれもNode.jsベースであり、JavaScriptを複数の抽象レイヤー(libuv、イベントループ、スレッドプール、システムコールの仲介)を通して実行しなければならない
- このとき引数変換、ワーカープールのキュー、イベントループ上のタスク分岐、futex(ロック同期)システムコールなどが積み重なり、IOそのものよりもシステムコール管理のほうが遅くなる結果を招く
- Node.jsで作られたパッケージマネージャーは、この構造的な限界のため、実際にネイティブに近い性能を出すのが難しい
Bun: Zigで実装されたネイティブインストールエンジン
- BunはZig言語で直接システムコールを呼び出し、JavaScriptエンジンや抽象化レイヤーをすべて省いている
- たとえばファイル読み込みでは、Zigコードからそのまま
openat()システムコールを実行し、即座にデータを返す
- そのため数万個のファイルを読む過程も、別途スレッドプール・イベントループ・データ変換を挟まず超高速に動作する
- ベンチマークでは、Bunは1秒あたり146,057個のpackage.jsonを読め、Node.jsは6万個台で2倍以上遅い
依存関係管理とDNS最適化
- Bunは
bun install実行時、依存関係の解析と同時にDNS prefetchを非同期でトリガーする
- たとえばmacOSではAppleの非公式async DNS API(
getaddrinfo_async_start())を使い、スレッドをブロックせずにネットワーク処理を同時実行できるようにしている
- 既存のパッケージマネージャーはlibuvスレッドプールベースで、実際には内部でブロッキングコードが走るため、リソースの無駄が発生する
パッケージマニフェストのバイナリキャッシュ
- npmなどはマニフェストをJSONでキャッシュするが、Bunは一度パースした後、その結果をバイナリ(
.npmファイル)に変換して保存する
- 文字列重複とパースオーバーヘッドを最小化し、実メモリ上ではオフセット計算だけで即座に値へアクセスできる(新規オブジェクト生成、パース、ガベージコレクションが不要)
- ETagとIf-None-Matchヘッダーで変更点だけを確認し、不要なデータパースなしに最新性を検証できる
- ベンチマークでは、Bunのキャッシュインストールはnpmのfresh installよりも速い
Tarball(圧縮ファイル)処理性能
- 一般的なパッケージマネージャーはtarballをストリームで受け取り、バッファメモリが不足するたびに再割り当て・コピー・リサイズが連続して発生する
- Bunはtarball全体を受信してから展開し、gzip末尾4バイトから展開後サイズを事前に把握して、メモリを一度だけ割り当てる
- libdeflateなどを活用して高速に展開し、不要な重複コピーやサイズ変更をすべて排除している
依存関係グラフとデータ構造の最適化
- 既存のパッケージマネージャーはJavaScriptオブジェクトやポインタベースの依存ツリーを作るため、メモリがランダムに分散し、CPUキャッシュミスが頻発する(pointer chasing問題)
- Bunは**Structure of Arrays(SoA)**パターンを適用し、すべてのパッケージ、文字列、依存関係を大きな連続メモリ領域に保存している
- オフセットと長さベースのアクセスにより、CPUは一度に複数のパッケージをキャッシュライン単位で読める(キャッシュフレンドリーな構造)
- lockfileもJSON/YAMLではなくSoAパターンに合わせ、文字列重複を除去しつつ順次メモリアクセスしやすい形で保存する
- lockfileのバイナリ形式(
bun.lockb)も試験的に導入されたが、Gitでの共同作業性が下がるため、可読性の高いプレーン形式へ切り替えられた
OS別ファイルコピー最適化
macOS
- clonefileを使用: ディレクトリ全体をCopy-On-Write方式で1回のシステムコールで複製する
- ディスク容量の重複使用を最小限に抑えつつ、インストール速度を最大化する
- clonefileが失敗した場合は、フォールバックとしてper-directory cloning→copyfileへ段階的に切り替える
Linux
- ハードリンクを優先して試行: 新しいファイルを生成せず、既存ファイルへの新しい参照だけを作る(ディスク上のデータ移動なし)
- ハードリンク不可の場合、Btrfs/XFSでは
ioctl_ficloneでCopy-On-Writeを適用する
- その後は
copy_file_range、sendfile、最後に通常のcopyfile方式へとフォールバックする
総評
- Bunはシステムコールの最小化、バイナリ構造、OS最適化、データ構造改善によって、パッケージマネージャーの従来の性能限界を超えている
- その結果、超高速なインストールだけでなく、メモリ効率とCPU効率も改善している
- 既存のNode.jsベースのマネージャーに比べ、別のランタイムへ置き換えなくてもプロジェクトへ適用できる(互換性を維持)
- 実際の大規模コードベースでは、数分かかっていたインストール工程を数ミリ秒〜数秒以内に短縮する体験を提供する
- システム、ハードウェア、OSレベルに合わせた最適化の優れた事例として、研究・参考価値が高い
1件のコメント
Hacker Newsのコメント
自分が使っているM4 Max MacBookが、2009年時点ならTOP500スーパーコンピュータの50位以内に入っていたはずだという主張を検証してみた。
2009年のTOP500に入るには75 TFlop/s以上の性能が必要。
M4 MaxはFP32で18.4 TFlop/sだが、TOP500はFP64(LINPACK)を使う。
M2のベンチマークを基準にすると、FP64はFP32の1/4程度なので、おおよそ9 TFlop/sと見積もられる。
その程度では2009年のTOP500には入れない。
参考: 2009年TOP500一覧
各接続が同時に複数のI/O処理を行うなら、数千の接続数を掛け合わせて考える必要がある。
サーバーは全体時間の95%をI/O待ちしているとよく言われるが、実際にはサーバー全体ではなく個々のスレッド基準の話だ。
実際のサーバーはCPU使用率が70〜80%まで上がることが多い(これを超えるとtail latencyが急激に悪化する)。
フルロードでCPU使用率が5%しかないなら、並列プロセス不足かメモリ不足の問題だ。
技術的には細かな点だが、こういうミスは投稿全体の信頼性を下げかねない(Bunのファンとして言っている)。
あの結論は、どこかLLMが作り出した幻覚のように感じる。
特に結論部分がLLMからそのまま出てきたような印象だった。
「ベンチマークされたパッケージマネージャーが間違っていたのではなく、当時に適した解法だったことが理解できた」
「Bunのやり方は革命的というより、現在速度を遅くしている原因を冷静に見つめた結果であることが強調されている」
「パッケージインストールが25倍速くなったのは魔法ではなく、最新ハードウェアに合わせてツールを作ったことで自然に起きた現象だ」
複雑なテーマなのに、とても読みやすくシンプルに説明されていて本当によかった。
いまでも情熱ある人たちが現状維持を打ち破って難しい課題に挑んでいることに感心する。
毎月コンピューターハードウェアは進歩しているのに、ソフトウェアのほうはむしろ遅くなっていくのは不自然に感じる。
みんながもっと効率の良いコーディングをできるようになってほしい。
Zigはかなり新しい言語なので、実務で本格的に使われているのを見るのは興味深い。
bunを初めて使ってみたが、とても印象的だった。
内蔵サーバーとSQLiteのおかげで、bunだけ入れればよく、開発がずっと楽になる。
普段はvanilla jsしか使わず、Nodeのエコシステムはあまり好きではなかったが、もっと早くbunを試すべきだったと思う。
Bunは何度か試したが、使い心地は非常に良かった。
Nodeより良いと感じた。
ただ、毎回決定的な問題にぶつかって結局Nodeに戻ることになる。
最初はcryptoモジュールがNodejsと互換性がなく(今は修正済み)、次はPlaywrightがBunで動かなかった。
最近のNodeも内蔵サーバーとSQLiteをサポートしている。
さらに多くの機能が必要なら、Honoも良い代替案だ。
記事でLinuxのハードリンクとMacOSのclonefileが同等だと説明していた部分がよく分からなかった。
ハードリンクの場合、1つのコピーを変更すると、すべてのプロジェクトのファイルが予期せず変わってしまうのではないかと思った。
技術的にはかなり複雑な説明なのに、本当に読みやすくて軽快に書かれていることに感心した。
彼女の作品や動画は大半を見たが、深く準備しているのが伝わってくる。
時間があれば、彼女の記事やYouTubeコンテンツを強くおすすめしたい。
最近はたぶん今の職場の都合で活動が減っているようだ。
Binary Manifest Cachingのセクションで、「npm (cached)」のベンチマーク時間が抜けているように見える。
bun、bun (cached)、npmしかなく、要約統計もきちんと一致していないようだ。
この投稿の文体がとても気に入った。
io_uringの重要性を説明するうえで、この文章はとても良い事例として再利用できそうだ。
Zigの最近のv0.15 ioアップデートが、Bunの性能に追加の恩恵をもたらすのか気になる。
1年以上bunに期待してきた。
2025年がbun普及元年になると思っていたが、意外にもまだそこまで人気ではない。
GitHub上位10万件のレポジトリでは、2025年時点の新規レポでnpmは35倍、pnpmは11倍多く使われている。
Denoも思ったほど人気が高くない。
なぜなのか気になる。
ランタイムはパッケージマネージャーより互換性を合わせるのが難しいからだろうか。
bunを試したが採用しなかった人たちの意見を聞いてみたい。
関連する参考統計
関連するHNコメント
BunもDenoも好きになりたくて何度も試したが、結局致命的な欠陥に当たって使い続けられなかった経験がある。
最近Bunで遭遇した最大の問題は、ストリームが早期に閉じる不具合だった。
関連Issueへのリンク
Denoではメモリリークの問題に遭遇した。
関連Issueへのリンク
結局、NodeのエコシステムがBun/Denoの利点を先に取り込んでいくのではないかと思っている。
Bunは新たなベンチャーキャピタル資金が入った、実績のあるオープンソースの本流製品(Node)と競争する新興勢力だ。
ロックインの誘因があり、結局のところNodeと根本的に大きく違うわけではない。
戦略的な強みが明確ではなく、Nodeではできない何か新しいものを提供しているわけでもない。
実際、真剣に使われている例は見たことがなく、軽く使われている例しか見ていない。
Issueトラッカーを見ると、Zigという言語がかなり安全でないせいなのか、クラッシュが頻繁に起きている。
自分はNodeに残るつもりだ。
自分も他の人の意見を聞いてみたい。
自分の考えでは、Nodeはプロジェクトとして成熟していて、民主的で、コミュニティ主導の色が強い。
io.jsのフォーク騒動もきちんと乗り越えたからだ。
それに対してbunやdenoはどちらもVC支援を受けているプロジェクトなので、民主的なコミュニティ主導には感じられない。
自分はBunの熱烈なファンだ。
可能な限りあらゆるプロジェクトでBunを使い、ちょっとしたone-offスクリプトもBun/TSで書いている。
ただし、少数ながら気になるIssueがあるので、本番デプロイはまだためらっている。
たとえば、単純なExpressウェブサーバーをDockerで立ち上げたとき、bunで動かすと停止する現象があった。
nodeに切り替えるだけで正常に動く。
1年前にはBun + Prismaの組み合わせでメモリリークによりサーバーが落ちる現象もあった(たぶん今は直っていると思う)。
それでもBunがとても気に入っているので、こうした欠点を受け入れても全体として開発時間を短縮してくれる。
トランスパイル、モジュール、ワークスペースなどに由来する利便性が非常に大きい。
まだnpmほど普及していないのも十分理解できる。
この記事を読むのは本当に楽しかった。
ソフトウェア開発においてコンピューターサイエンスの原理が実際にどれほど重要かをよく示す事例だ。
Big O、時間的・空間的局所性、アルゴリズム複雑性、ユーザー/カーネル空間、ファイルシステム、copy-on-writeなど。
こうした低レベルのパッケージ開発では、CSプログラムで学ぶあらゆる概念が実際に活用される。
CSは計算と理論(プログラミング言語、アルゴリズム、暗号、機械学習など)を研究し、
SEはスケーラブルで信頼性の高いソフトウェアを構築するための工学原則を適用するものだ。
圧縮ファイルを全部読み終えてから展開を待つのが、なぜ有利なのかよく分からない。
ダウンロードが終わる前から展開を始めたほうが、メモリ再コピー回数が増える不利益よりむしろ得のほうが大きいのではないかと推測している。