世界を動かす技術を、日本語で。

「Bun Install」の舞台裏

概要

  • Bun はパッケージインストールが非常に高速
  • npm/pnpm/yarn よりも圧倒的に少ないシステムコール数
  • システムプログラミング的手法 で徹底的な最適化を実現
  • Node.js由来の従来手法 の限界とBunの革新性
  • 実際のベンチマーク で示される圧倒的な速度差

Bunのパッケージインストールが圧倒的に速い理由

  • Bun はパッケージインストール時、 npmの約7倍、pnpmの約4倍、yarnの約17倍 の速度を実現
  • 特に 大規模コードベース で顕著な高速化
  • 従来の数分かかる作業が 数秒(ミリ秒) で完了
  • 単なるベンチマークではなく、 システムレベルの最適化 による本質的な高速化
  • パッケージインストール を「JavaScriptの問題」ではなく「システムプログラミングの問題」として捉える姿勢

Node.js時代の設計と現在のギャップ

  • Node.js は2009年に登場し、 I/O待ち が主なボトルネックだった時代に最適化
  • JavaScriptの イベントループ を活用し、非同期I/Oを効率化
  • 2009年当時は ディスクシーク10ms、DBクエリ50-200ms、HTTPリクエスト300ms超 が当たり前
  • サーバーはI/O待ちで CPUの95%を浪費 していた
  • しかし現在は ハードウェア進化 により、I/O待ちよりも システムコールのオーバーヘッド が主なボトルネックに

システムコールの問題点

  • アプリケーションがOSにアクセスするたびに システムコール が発生
  • ユーザーモードカーネルモード の切り替えは 1000-1500サイクル のオーバーヘッド
  • 3GHz CPUで0.5マイクロ秒程度だが、 大量のシステムコール が発生すると膨大なCPU時間を消費
  • 例えばReactインストールで 5万回以上のシステムコール が発生し、モード切り替えだけで数秒のCPU時間を浪費

パッケージマネージャーごとのシステムコール比較

  • Bun: 165,743回(29.5k/秒)
  • pnpm: 456,930回(18.6k/秒)
  • npm: 996,978回(26.8k/秒)
  • yarn: 4,046,507回(43.0k/秒)
  • futexコール (スレッド同期用)もBunは極端に少ない(Bun: 762回、npm: 663,158回、yarn: 2,499,660回、pnpm: 116,577回)

Bunのアプローチと他ツールの違い

  • npm/pnpm/yarn はすべて Node.js製 で、 libuv 経由でシステムコールを間接的に実行
    • JavaScript→libuv→スレッドプール→カーネルモード→戻る、という複雑なパイプライン
    • 各fs.readFileごとにスレッド同期やロック取得でfutexコール多発
  • BunZig製 で、 ネイティブコードから直接システムコール を実行
    • 余計なランタイムやスレッドプール、イベントループを経由せず、最短経路でOSにアクセス
  • その結果、 package.jsonファイルの読み込み速度 も圧倒的(Bun: 0.019ms/件、Node.js: 0.065ms/件)

Bunのインストール最適化事例

  • DNS解決の非同期化 (macOS限定最適化)
    • package.json解析中に並行してDNSルックアップを先行実行
  • 依存関係解析やファイル操作 もすべてネイティブなシステムコールで高速処理
  • Node.jsプロジェクトでもbun install利用可能 で、既存のエコシステムを壊さず高速化

まとめ

  • Bunはシステムプログラミング的発想 でパッケージ管理を再定義
  • 徹底したシステムコール削減ネイティブ最適化 で、従来のNode.js系パッケージマネージャーを圧倒
  • 現代のハードウェア性能 をフル活用し、インストール体験を劇的に向上
  • Node.js依存のツール設計 が抱える構造的な遅さを根本から解決するアプローチ

Hackerたちの意見

先週、初めてbunを使ってみたんだけど、めっちゃ良かった!内蔵サーバーとSQLiteのおかげで、bun自体以外の依存関係がいらなかったのが、開発するには最高の方法だよね。ノードエコシステムが嫌いなのに、ほとんどの開発をバニラJSでやってるから、もっと早く試しておけばよかったな。

bunを何回か使ってみたけど、すごく楽しかった!ノードより全然いい。ただ…!bunでいつも行き詰まって、結局ノードに戻っちゃった。最初はcryptoモジュールがNode.jsの署名と互換性がなかった(今は修正済み)、次はPlaywrightがCrawlee経由でbunと動かなくなった。

でも最近のNodeにも内蔵サーバーやSQLiteがあるよね?それとも、もっと多機能が必要なら、Honoがいいよ。

bunには約1年間ワクワクしてたんだけど、2025年がブレイクアウトの年になると思ってる。もっと人気がないのが意外だよね。GitHubのトップ10万リポジトリを見たら、2025年の新しいリポジトリではnpmが35倍、pnpmが11倍もbunより人気だった。もう一つの注目のJavaScriptランタイムであるdenoもあんまり人気ないし、なんでだろう?ランタイムだから、互換性を持たせるのがパッケージマネージャーより難しいのかな?bunを試してみたけど、個人的にも仕事でも採用しなかった人が理由を教えてくれたら嬉しいな。

これについてみんながどう思ってるのか、すごく気になる。私にとって、Nodeは成熟していて、民主的で、コミュニティ主導のプロジェクトって感じがする。特に数年前のio.jsフォークのドラマをうまく乗り越えた後はね。bunもdenoも、どちらもVC資金で運営されてるから、コミュニティ主導の民主的なプロジェクトではないのは明らかだよね。

問題の一部は、変更がかなり漸進的で、NodeJSに戻すのが簡単だからだと思う。あるいは、bunを始めるのを楽にするようなものだけど、長期的な価値はあまり加えないってこともある。例えば、コメントで誰かがsqliteモジュールとhttpサーバーについて話してたけど、今はNodeJSもsqliteをネイティブでサポートしてるし、ウェブ開発でサーバーを書くなら、ExpressやFastifyのような既存の、実績のあるフレームワークを使いたいな。クールなプロジェクトだし、V8を使わずに違うことに挑戦してるのはいいと思うけど、そんな漸進的な改善を売り込むのは難しいと思う。

これは新しい、VC資金で運営されているオープンソースの実績ある支配的プレイヤーへの競争相手だよね。ロックインするインセンティブがあって、結局ノードとあまり変わらない。bunを使うことに戦略的な利点はほとんどないし、ノードでできないことを実現するわけでもない。真剣に選んでる人はまだ見たことないけど、真剣じゃない人が使ってるのはよく見かける。

彼らのイシュートラッカーを見てみて。クラッシュがいっぱいで、どうやらこのZig言語はかなり危険みたい。俺はNodeに留まるよ。

BunもDenoも特に決定的な特徴はないよね。確かにNodeにも追加すべきいい機能はあるけど、エコシステムの変更や破損に対処するほどの魅力は感じないな。

俺はBunの一番のファンだよ。できるだけ毎プロジェクトで使ってるし、単発のスクリプトもBun/TSで書いてる。でも、プロダクション環境に導入するのはちょっと不安になる問題に遭遇したこともある。例えば、少し前にDocker内のExpressウェブサーバーがハングしちゃったことがあったけど、Nodeに切り替えたら問題なかった。1年前にはBun + Prismaのウェブサーバーがメモリリークしてクラッシュすることもあった。(もう1年経ったし、たぶんそれは修正されてると思う。)実際、Bunはすごく良くて、こういう面倒があっても時間を節約できると思う。トランスパイレーションやモジュール、ワークスペース周りの頭痛の種を解決してくれるのは本当に素晴らしい。でも、npmに近づいていない理由も理解できるよ。

既存のものを打ち負かすには、2倍良くならなきゃダメだよ。今のところ、合理的なサイズのプロジェクトに対しては1.1倍良くなってる進行中の状態で、進行中に期待される問題やエコシステムの信頼性が疑問視されてる。趣味のプロジェクトや小さな新規プロジェクトにはいいかもしれないけど、真剣な会社のプロジェクトをリスクにさらすつもりは全くないね。

BunとDenoを好きになりたいんだけど、何度か使ってみたけど、決定的な問題にぶつかるまでに数千行以上書いたことがないんだ。Bunで最後に大きな問題があったのはストリームが早く閉じちゃったことだね:https://github.com/oven-sh/bun/issues/16037 Denoでの最後の大きな問題はメモリリークだった:https://github.com/denoland/deno/issues/24674 今のところ、NodeエコシステムがBun/Denoの良い部分を取り入れる前に、Bun/Denoが本当に普及することはない気がする。

昨年試してみたけど、内蔵のsqliteドライバーと格闘するのに数時間かかったよ。バグも多くて(サイレントエラー)、ドキュメントも全然足りなかった。

まだいくつかの互換性の問題があるね… Denoにはかなり詳しくて、ここ数年ずっと使ってるから、今やデフォルトのシェルスクリプトツールになってる。でも、仕事のプロジェクトではMS-SQLにアクセスする必要があって、Denoのランタイムではソケット接続のやり方がサポートされてないんだよね。これが仕事でできることを制限してる。Bunでも他のモジュールやツールに似たような問題があると思う。エントロピーから抜け出すのもすごく難しいし。Node+npmは10年以上かけてそのエコシステムを築いてきたから、みんな簡単には手放せないよね。シェルスクリプトにはDenoが好きで、シェバンを使ったり、依存関係を参照したりすると、ランタイムがそれを処理してくれるから楽なんだ。「npm install」って別に実行する必要もないし、~/bin/ディレクトリが潜在的に競合するnode_modulesで汚れることもない。共有の(設定可能な)場所から使えるからね。Bunも似たような感じだと思う。ただ、仕事ではすでにあるシステムを使わなきゃいけないから、気まぐれで技術を置き換えるわけにはいかないんだよね。

Node.jsはlibuvを使っていて、これはプラットフォームの違いを抽象化し、スレッドプールを通じて非同期I/Oを管理するCライブラリです。 > bunは違うやり方をしています。bunはZigというプログラミング言語で書かれていて、ネイティブコードにコンパイルされ、直接システムコールにアクセスできます。つまり、C/C++もネイティブコードにコンパイルされるんだよね。彼らの言ってることは理解できるし、いいことだと思うけど、nodejsも多分それができたはずなのに、やらなかった。でも、あたかもそれが本質的にできないかのように言わないでほしい。npmがこの抽象化を使うことを強制されたわけじゃないし、npmは最初からC/C++でnodejsのアドオンにすべきだったと思う。(これがnpmやnodeを擁護してるように聞こえたら、そうじゃないからね。)

私にとっての理由はこうだと思う:npm、pnpm、yarnはJSで書かれているから、libuvに基づくNode.jsの機能を使わざるを得ない。これはこの場合最適ではない。bunはZigで書かれているから、libuvが必要なくて、自分のことができる。もちろん、誰かがNode.jsのパッケージマネージャーをC/C++でネイティブモジュールとして書くこともできるけど、npm、pnpm、yarnはそうしなかったんだよね。

問題はlibuvがCだからじゃなくて、それを呼び出すのがJavaScript(Node.js)だから、libuvがシステムコールをするたびにモードを切り替えなきゃいけないってことじゃない?

バイナリマニフェストキャッシングセクションに「npm(キャッシュ済み)」のベンチマーク時間を入れ忘れたんじゃないかな。bun、bun(キャッシュ済み)、npmがあるけど、要約統計も間違ってる気がする。

Denoがこれにどう立ち向かうのかちょっと気になるな…それに、どんなパッケージがインストールされてるのかもわからない。俺は多分、react+ts+muiのためにviteテンプレートプロジェクトを基準に始めると思う。これはツールにとって比較的典型的なアプリケーションの組み合わせだからね。もしかしたらhono+zod+openapiも使うかも。

Denoがベンチマークに含まれてないのは、思ったより比較が難しいからだと思う。Denoの依存関係のアーキテクチャはnpmを中心に作られてないし、その互換性レイヤーはコアの上に後付けされてるんだ(ソースコードを見ればわかるよ)。Denoの依存関係管理のコアアーキテクチャは、URLベースの異なるパラダイムを使ってる。速さはあんまりだけど…違うんだよね。それに、セキュリティが向上したり、自分の安全なレジストリを簡単にホストできるようなクールな機能もある。npmやjsrを使う必要はないし、すごく面白いけど、ここでベンチマークされてるものとは違うんだ。

こんなに技術的な説明が読みやすくて楽しいとは驚きだよ。執筆お疲れ様!

ほんとに!リディアは複雑なアイデアをシンプルに、しかも上手に伝えるのが得意だよね。彼女の作品や動画はほとんど見たり読んだりしたけど、すごく手間をかけて生き生きとした内容にしてるのがわかる。彼女の記事やYouTube動画はほんとにおすすめ!ただ、今の仕事のせいであんまり書いてないみたいだけど。

複雑なテーマなのに、すごくシンプルに読めるね。著者におめでとう!それと、情熱的な人たちがまだ存在して、難しいことに挑戦しているのが好きだな。自分には考えることすらできないようなことにね。毎月コンピュータは良くなってるのに、ソフトウェアは遅くなるのはおかしいよね。みんな(自分も含めて)もっと効率的なコードを書くのが上手だったらいいのに。

ゼグで書かれてるなんて知らなかった!この言語がまだ若いのに、そんな選択をするのが面白いね。実際にプロダクションで使われてるのを見るのはすごいことだよ。

この投稿の文体がすごく好きだった。いくつか気になったことがあるんだけど: - この投稿を再利用すれば、io_uringがなぜそんなに重要なのかを説明するのに良いかも。 - Zigの最近のv0.15でのioアップデートが、Bunの現在の速さを超えるパフォーマンス向上をもたらすのか気になる。

わお、yarnがこんなに遅いなんて驚きだよ。以前はnpmよりもずっと速かったのに。私がいた会社では、npmからyarn、pnpm、またnpmに戻ったんだ。最近はできるだけBunを使うようにしてるけど、VercelはNextのためにネイティブではまだ使ってないんだよね。

なんでpnpmをやめたの?

今これを書いてるM4 Max MacBookは、2009年には地球上で50番目に速いスーパーコンピュータの中に入ってたかもしれない。これを確認しようとしたんだけど、2009年のTOP500ランキングでトップ50に入るには>75 TFlop/sが必要だったんだ。M4 MaxのレビューではFP32で18.4 TFlop/sって書いてあるけど、TOP500はLINPACKを使ってて、FP64の精度を使うんだ。M2のベンチマークでは倍精度で1:4の比率が出るから、FP64だと多分9 TFlop/sくらいになるのかな?それだと2009年のTOP500には入れないね。[0]: https://top500.org/lists/top500/list/2009/06/

それを何千もの同時接続がそれぞれ複数のI/O操作を行ってると考えてみて。サーバーはI/O操作を待ってる時間が約95%だって。いや、特定の実行スレッドはI/Oを待ってる時間が95%だったかもしれないけど、サーバー(何千もの接続を提供してるマシン)はCPUの利用率が簡単に70%-80%で動いてるよ(それ以上になると、遅延がひどくなってくるから)。もしサーバーがフルロードでCPU利用率が5%だったら、十分な並列プロセスを動かしてないか、そうするためのRAMを十分にインストールしてないってことだよ。まあ、これは技術的なことだけど、投稿は技術的なことに専念してるから、こういう小さなミスが他の部分への信頼を損なうんだよね。(僕は文のファンとして言ってるんだけど。)