Bunとは

Bunは、最近注目を集めている次世代のJavaScriptランタイムです。Node.jsやDenoと同様にサーバーサイドのJavaScript実行環境を提供しますが、高速な起動統合ツール群(バンドラー、トランスパイラー、パッケージマネージャ)が特徴です。BunのコアはZig という言語で実装されており、パフォーマンス面での優位性を発揮しています。

Bunのここがすごい!

高速性

Node.jsと比較して、サーバの起動時間やリクエスト処理速度が数倍高速です。

統合ツール群

Node.js単体ではランタイムのみを提供し、パッケージ管理(npmやYarn)、バンドラー(webpack、Rollupなど)、トランスパイラー(Babelなど)は別途導入する必要がありますが、BunはそれらすべてBunの中にまとまって提供されます。

TypeScript対応

最新のECMAScript機能をサポートしており、TypeScriptもネイティブに扱えます。TypeScriptをトランスパイルなしで直接実行可能なのでNode.jsではts-nodeなどを追加でインストールする必要がありますが、Bunならその手間が不要です。

ホットリロード対応

`bun dev` コマンドを使って立ち上げた開発サーバに、コードの変更をリアルタイムで反映できます。

テストランナー実装

Node.jsを使用する場合はJestのような外部のテストフレームワークを使ってテストしますが、Bun独自のテストランナーを備えているため高速かつシンプルにテストの実行が可能

用語解説

ランタイム

ランタイムとは、プログラムが「実行」される環境のことです。
たとえば、JavaScriptのランタイムとしては、ブラウザ環境が挙げられますが、サーバサイドではNode.jsやBunなどがこれに該当します。これらは、JavaScriptコードを実行するために、独自のAPIやシステムリソース管理の仕組みを備えています。

バンドラー

バンドラーとは、複数のJavaScriptモジュールやその他のリソース(CSS、画像など)を解析し、1つまたは少数のファイルにまとめるツールのことです。

トランスパイル

ソースコードを解析し、構文や機能を理解した上で、目的の環境に適したコードに再生成することです。例えば、型付けされたTypeScriptコードを、ブラウザやNode.jsで実行可能なJavaScriptに変換すること。

前準備

Bun と Node.js の比較をするために、まずは Docker で Bun と Node.js のコンテナを作成していきます。

Bun の Dockerfile

FROM oven/bun:latest

WORKDIR /app

COPY package.json .
COPY server.ts .

RUN bun install

EXPOSE 3001

CMD ["bun", "run", "server.ts"]

Node.js の Dockerfile

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
COPY tsconfig.json ./
COPY server.ts ./

RUN npm install

EXPOSE 3002

CMD ["npm", "start"]

docker-compose.yml

version: "3"

services:
  bun-server:
    build:
      context: ./bun-server
      dockerfile: Dockerfile
    ports:
      - "3001:3001"
    container_name: bun-server

  node-server:
    build:
      context: ./node-server
      dockerfile: Dockerfile
    ports:
      - "3002:3002"
    container_name: node-server

Bun で HTTP サーバーを作成

では早速 Bun を使用して HTTP サーバーを立てていきましょう。

// server.ts
import { Server } from "bun";

const server: Server = Bun.serve({
  port: 3001,
  fetch(request: Request): Response {
    return new Response("Hello from Bun Server!");
  },
});

console.log(`Bun server listening on ${server.url}`);

非常にシンプルな記述で HTTP サーバーを立てることができました。

Node.js で HTTP サーバーを立ててみる

では次は Node.js を使用して HTTP サーバーを立てていきましょう。

// server.ts
import { createServer, IncomingMessage, ServerResponse } from "http";

const server = createServer((req: IncomingMessage, res: ServerResponse) => {
  res.statusCode = 200;
  res.setHeader("Content-Type", "text/plain");
  res.end("Hello from Node.js Server!");
});

const PORT: number = 3002;
server.listen(PORT, () => {
  console.log(`Node.js server listening on port ${PORT}`);
});

Bun と Node.js のパフォーマンス比較

コンテナの立ち上げ

まずは上記のコードをもとにコンテナを立てて HTTP サーバーを立ててみましょう。

docker-compose up -d

ブラウザで`http://localhost:3001`と`http://localhost:3002`にアクセスして以下のように各サーバーが立ち上がったら成功です。

↓Bunサーバー

↓Node.jsサーバー

ベンチマークテストツール

今回ベンチマークテストツールとして`autocannon`を使用します。

`autocannon`は Node.js の HTTP サーバーのパフォーマンスを計測するためのツールです。(Bun にも使えます)

npm install -g autocannon

Bun サーバーと Node.js サーバーのベンチマークテストを実行してみましょう。

ベンチマークテスト

Bun サーバーと Node.js サーバーのベンチマークテストを実行してみましょう。

autocannon -c 100 -d 30 http://localhost:3001
autocannon -c 100 -d 30 http://localhost:3002

これは各対象の HTTP サーバーに 100 個の同時接続して 30 秒間リクエストを送信してパフォーマンスを計測するコマンドです。

結果を見てみましょう。

PS C:\project\Bun> autocannon -c 100 -d 30 localhost:3001
Running 30s test @ http://localhost:3001
100 connections


┌─────────┬──────┬───────┬───────┬───────┬──────────┬─────────┬────────┐
│ Stat    │ 2.5% │ 50%   │ 97.5% │ 99%   │ Avg      │ Stdev   │ Max    │
├─────────┼──────┼───────┼───────┼───────┼──────────┼─────────┼────────┤
│ Latency │ 8 ms │ 11 ms │ 17 ms │ 19 ms │ 11.76 ms │ 4.18 ms │ 251 ms │
└─────────┴──────┴───────┴───────┴───────┴──────────┴─────────┴────────┘
┌───────────┬────────┬────────┬─────────┬─────────┬─────────┬─────────┬────────┐
│ Stat      │ 1%     │ 2.5%   │ 50%     │ 97.5%   │ Avg     │ Stdev   │ Min    │
├───────────┼────────┼────────┼─────────┼─────────┼─────────┼─────────┼────────┤
│ Req/Sec   │ 5,431  │ 5,431  │ 8,191   │ 9,095   │ 8,201.8 │ 659.22  │ 5,431  │
├───────────┼────────┼────────┼─────────┼─────────┼─────────┼─────────┼────────┤
│ Bytes/Sec │ 750 kB │ 750 kB │ 1.13 MB │ 1.25 MB │ 1.13 MB │ 90.9 kB │ 749 kB │
└───────────┴────────┴────────┴─────────┴─────────┴─────────┴─────────┴────────┘

Req/Bytes counts sampled once per second.
# of samples: 30

246k requests in 30.23s, 34 MB read
PS C:\project\Bun>
PS C:\project\Bun>
PS C:\project\Bun> autocannon -c 100 -d 30 localhost:3002
Running 30s test @ http://localhost:3002
100 connections


┌─────────┬───────┬───────┬───────┬───────┬──────────┬─────────┬────────┐
│ Stat    │ 2.5%  │ 50%   │ 97.5% │ 99%   │ Avg      │ Stdev   │ Max    │
├─────────┼───────┼───────┼───────┼───────┼──────────┼─────────┼────────┤
│ Latency │ 11 ms │ 15 ms │ 30 ms │ 33 ms │ 15.95 ms │ 5.47 ms │ 158 ms │
└─────────┴───────┴───────┴───────┴───────┴──────────┴─────────┴────────┘
┌───────────┬────────┬────────┬─────────┬────────┬──────────┬────────┬────────┐
│ Stat      │ 1%     │ 2.5%   │ 50%     │ 97.5%  │ Avg      │ Stdev  │ Min    │
├───────────┼────────┼────────┼─────────┼────────┼──────────┼────────┼────────┤
│ Req/Sec   │ 3,553  │ 3,553  │ 6,235   │ 6,847  │ 6,080.77 │ 577.53 │ 3,552  │
├───────────┼────────┼────────┼─────────┼────────┼──────────┼────────┼────────┤
│ Bytes/Sec │ 622 kB │ 622 kB │ 1.09 MB │ 1.2 MB │ 1.06 MB  │ 101 kB │ 622 kB │
└───────────┴────────┴────────┴─────────┴────────┴──────────┴────────┴────────┘

Req/Bytes counts sampled once per second.
# of samples: 30

183k requests in 30.06s, 31.9 MB read

こちら上記結果をChatGPTくんに要約してもらったものです。

レイテンシー(応答時間):
3001(Bun): 平均11.76ms
3002(Node.js): 平均15.95ms
→ 3001の方が約35%速い

毎秒リクエスト処理数(Req/Sec):
3001(Bun): 平均8,201.8
3002(Node.js): 平均6,080.77
→ 3001の方が約35%多くのリクエストを処理

30秒間の総リクエスト数:
3001(Bun): 246,000リクエスト
3002(Node.js): 183,000リクエスト
→ 3001の方が約63,000リクエスト多く処理

こんな単純な処理を返すサーバーでもBunの方がNode.jsよりも高いパフォーマンスを発揮していることがわかりますね。

まとめ

BunはNode.jsよりも高速で、シンプルな記述でHTTPサーバーを立てることができます。

皆さんもぜひBunを使ってみてください。