Bun と Node.js で HTTP サーバーを立てて性能を比較してみた

## はじめに
普段はフリーランスエンジニアをしているRyuと言います！
今回は初めてBunを触った際、あまりにも動作が爆速で驚いたのでNode.jsとの比較をしてみました。

## 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

```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

```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

```yaml
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 サーバーを立てていきましょう。

```ts
// 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 サーバーを立てていきましょう。

```ts
// 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 サーバーを立ててみましょう。

```bash
docker-compose up -d
```

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

↓Bun サーバー
![20250301-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3577451/77f8e7ca-e9d8-4272-bf20-f3a298af42d8.png)


↓Node.js サーバー
![20250301-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3577451/c8019b04-bc81-494f-bbae-3901b67b645e.png)


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

今回ベンチマークテストツールとして`autocannon`を使用します。
`autocannon`は Node.js の HTTP サーバーのパフォーマンスを計測するためのツールです。(Bun にも使えます)

```bash
npm install -g autocannon
```

### ベンチマークテスト

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

```bash
autocannon -c 100 -d 30 http://localhost:3001
```

```bash
autocannon -c 100 -d 30 http://localhost:3002
```

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

結果を見てみましょう。

```bash
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くんに要約してもらったものです。

```bash
レイテンシー（応答時間）:
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を使ってみてください！

もし記事に追記した方がいいことや誤り等ありましたらご連絡ください！

個人ブログでも記事投稿しているので暇な時にでも覗いてやってくださいm(._.)m
https://ryu-engineer.com/