Mercurial
comparison benchmark/bun-http-framework-benchmark/README.md @ 186:8cf4ec5e2191 hg-web
Fixed merge conflict.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 23 Jan 2026 22:38:59 -0800 |
| parents | a8976a008a9d |
| children |
comparison
equal
deleted
inserted
replaced
| 176:fed99fc04e12 | 186:8cf4ec5e2191 |
|---|---|
| 1 # Bun HTTP Framework Benchmark | |
| 2 | |
| 3 Compare throughput benchmarks from various JavaScript HTTP framework | |
| 4 | |
| 5 Test method: Average throughput | |
| 6 | |
| 7 1. Ping | |
| 8 - Request to [GET] `/` | |
| 9 - Return `hi` | |
| 10 - Headers must contains text `Content-Type: text/plain`, additional context is acceptable eg. `Content-Type: text/plain; charset=utf-8` | |
| 11 2. Query | |
| 12 - Request to [GET] `/id/:id` | |
| 13 - Extract path parameter, query string and setting headers. | |
| 14 - For this benchmark, the request URL will be send as: `/id/1?name=bun` | |
| 15 - Headers must contains `x-powered-by` to `benchmark` | |
| 16 - Expected response: **"1 bun"** (`${id} ${query}`) | |
| 17 - You **MUST NOT use hardcode string or index** to extract querystring. | |
| 18 - In a real-world situation, there's no enforcement that the request will follow the specification, using hardcode index to extract `name=bun` querystring will be prone to error. | |
| 19 - To test if it pass the requirement, the implementation should be able to extract querystring **dynamically** (please treat the value of 'name=bun' can be any value beside 'bun', for example 'alice', 'hina'), which means that the same code should be able to extract querystring, for example: | |
| 20 - `/id/1?name=bun&id=1` -> should return `1 bun` not `1 bun&id=1` | |
| 21 - `/id/1?id=1` -> should return `1 ` | |
| 22 - Query beside `name` maybe not need to be extracted and is optional | |
| 23 - Headers must contains text `Content-Type: text/plain`, additional context is acceptable eg. `Content-Type: text/plain; charset=utf-8` | |
| 24 3. Body | |
| 25 - [POST] `/json` | |
| 26 - Mirror body to response | |
| 27 - Server **MUST parse body to JSON and serialize back to string** | |
| 28 - For the benchmark, the request body will be sent as: `{ "hello": "world" }` | |
| 29 - Expected response: `{ "hello": "world" }` | |
| 30 - Headers must contains text `Content-Type: application/json`, additional context is acceptable eg. `Content-Type: application/json; charset=utf-8`. | |
| 31 | |
| 32 ## requirement | |
| 33 | |
| 34 - The framework must at-least has latest published in less than 9 month otherwise will be classified as unmaintained and removed unless is an industry standard (Express). | |
| 35 | |
| 36 # Prerequistes | |
| 37 | |
| 38 - [bombardier](https://github.com/codesenberg/bombardier) | |
| 39 - Nodejs | |
| 40 - Deno | |
| 41 - Bun | |
| 42 | |
| 43 # Run Test | |
| 44 | |
| 45 ```typescript | |
| 46 bun benchmark | |
| 47 ``` | |
| 48 | |
| 49 Dump result will be available at `results/[benchmark-name].txt` | |
| 50 | |
| 51 ## Benchmark Condition | |
| 52 | |
| 53 This benchmark is tested under the following condition: | |
| 54 | |
| 55 - Intel I7-13700K, DDR5 32GB 5600MHz | |
| 56 - Arch Linux 6.10.9-arch1-2 (btw I use Arch) | |
| 57 - Bun 1.2.11 | |
| 58 - Node 22.15.0 | |
| 59 - Deno 2.2.0 | |
| 60 | |
| 61 Tested on 6 May 2025 20:55 (GMT+7) | |
| 62 | |
| 63 ## Results | |
| 64 | |
| 65 These results are measured in req/s: | |
| 66 | |
| 67 | Framework | Runtime | Average | Ping | Query | Body | | |
| 68 | ---------------- | ------- | ------- | ---------- | ---------- | ---------- | | |
| 69 | uws | node | 526,295.397 | 535,079.45 | 509,759.27 | 534,047.47 | | |
| 70 | ultimate-express | node | 416,645.31 | 526,907.98 | 508,436.34 | 214,591.61 | | |
| 71 | elysia | bun | 397,259.163 | 533,560.05 | 300,716.41 | 357,501.03 | | |
| 72 | bun | bun | 366,716.193 | 386,961.93 | 317,958.96 | 395,227.69 | | |
| 73 | hyper-express | node | 315,844.147 | 370,809.65 | 299,522.59 | 277,200.2 | | |
| 74 | bun-web-standard | bun | 303,568.81 | 338,462.84 | 255,297.65 | 316,945.94 | | |
| 75 | nhttp | bun | 283,734.777 | 373,377.37 | 244,767.65 | 233,059.31 | | wobe | bun | 282,858.987 | 282,509.03 | 250,687.47 | 315,380.46 | | |
| 76 | hono | bun | 253,646.173 | 294,945.14 | 236,066.25 | 229,927.13 | | |
| 77 | deno | deno | 247,770.227 | 269,520.16 | 233,460.27 | 240,330.25 | | |
| 78 | nbit | bun | 229,226.153 | 252,902.81 | 196,843.1 | 237,932.55 | | |
| 79 | deno-web-standard | deno | 204,908.633 | 236,235.96 | 183,458.05 | 195,031.89 | | |
| 80 | hono | deno | 201,548.523 | 250,592.98 | 195,744.04 | 158,308.55 | | |
| 81 | fastify | node | 142,695.487 | 155,142.07 | 148,078.23 | 124,866.16 | | |
| 82 | hono | node | 129,234.593 | 144,842.92 | 134,412.44 | 108,448.42 | | |
| 83 | express | bun | 126,674.41 | 145,427.54 | 134,805.54 | 99,790.15 | | |
| 84 | oak | bun | 120,481.7 | 117,743.45 | 109,295.56 | 134,406.09 | | |
| 85 | h3 | bun | 109,739.16 | 132,720.75 | 104,854.13 | 91,642.6 | | |
| 86 | h3 | node | 101,237.803 | 136,023.12 | 111,361.83 | 56,328.46 | | |
| 87 | oak | deno | 100,205.983 | 105,824.67 | 93,335 | 101,458.28 | | koa | node | 96,456.887 | 103,217.62 | 97,582.12 | 88,570.92 | | |
| 88 | acorn | deno | 64,951.337 | 95,734.91 | 71,099.63 | 28,019.47 | | |
| 89 | express | node | 25,079.01 | 26,286.22 | 24,796.49 | 24,154.32 | | |
| 90 | |
| 91 See more detail in [results](https://github.com/SaltyAom/bun-http-framework-benchmark/tree/main/results) | |
| 92 | |
| 93 ## Notice | |
| 94 | |
| 95 I highly recommended testing this benchmark on your machine yourself as performance in likely to vary between machine. | |
| 96 | |
| 97 If you are unable to run Deno, please run each Deno app individually first until the Deno finish installing the package, then proceed to run benchmark using `bench.sh` or `npm run benchmark` |