|
1 | 1 | # Performance Benchmarks |
2 | 2 |
|
3 | | -## Test Environment |
| 3 | +Benchmarks for the Deserve router + DVE view rendering. |
4 | 4 |
|
5 | | -- **Tool:** `autocannon` — [npmjs.com/package/autocannon](https://www.npmjs.com/package/autocannon) |
6 | | -- **Configuration:** 500 connections, 10 pipelining, 30 seconds (adjust per run) |
| 5 | +## Setup |
7 | 6 |
|
8 | | -## How to Run |
| 7 | +- **Load tool**: `autocannon` ([npm](https://www.npmjs.com/package/autocannon)) |
| 8 | +- **Default config used in this doc**: 500 connections, pipelining 10, duration 30s |
9 | 9 |
|
10 | | -**1. Start the server** (from repo root). Pick one: |
| 10 | +## Quick Start |
11 | 11 |
|
12 | | -- **Without worker** — `/test` and `/test-cpu` only; `/test-worker` returns 503: |
| 12 | +Start a server (from repo root), then run `autocannon` from another terminal. |
| 13 | + |
| 14 | +### Start Server |
| 15 | + |
| 16 | +- **Non-worker mode** (view routes + CPU on main thread): |
13 | 17 |
|
14 | 18 | ```bash |
15 | 19 | deno run --allow-net --allow-read benchmark/main.ts |
16 | 20 | ``` |
17 | 21 |
|
18 | | -- **With worker** — all routes including `/test-worker`: |
| 22 | +- **Worker mode** (enables `/test-worker`): |
| 23 | + |
19 | 24 | ```bash |
20 | 25 | deno run --allow-net --allow-read benchmark/main-worker.ts |
21 | 26 | ``` |
22 | 27 |
|
23 | | -**2. Run autocannon** (in another terminal; requires Node/npx): |
| 28 | +Notes: |
| 29 | + |
| 30 | +- In non-worker mode, **`/test-worker` returns 503** (`worker not enabled`). |
| 31 | +- All view routes (`/test-view*`) are available in both modes. |
| 32 | + |
| 33 | +### Run Benchmark |
24 | 34 |
|
25 | 35 | ```bash |
26 | 36 | npx autocannon http://localhost:8000/test -c 500 -p 10 -d 30 |
27 | 37 | ``` |
28 | 38 |
|
29 | | -## Files |
| 39 | +## Routes |
| 40 | + |
| 41 | +### JSON + CPU Comparison |
30 | 42 |
|
31 | | -- **`benchmark/main.ts`** — Router without worker. Serves `/test`, `/test-cpu`. |
32 | | -- **`benchmark/main-worker.ts`** — Router with worker pool (inline CPU loop). |
33 | | -- **`benchmark/routes/test.ts`** — GET `/test`: baseline, JSON only (no CPU work). |
34 | | -- **`benchmark/routes/test-cpu.ts`** — GET `/test-cpu`: same CPU work (50k sqrt loop) on **main thread**. |
35 | | -- **`benchmark/routes/test-worker.ts`** — GET `/test-worker`: same CPU work offloaded to **worker**. |
| 43 | +| Route | What it does | Why it exists | |
| 44 | +| -------------- | ---------------------------------------- | -------------------------- | |
| 45 | +| `/test` | JSON only (no CPU work) | Baseline throughput | |
| 46 | +| `/test-cpu` | 50k `sqrt` loop on **main thread** | Event-loop blocking cost | |
| 47 | +| `/test-worker` | Same loop offloaded to a **worker pool** | Offload overhead + scaling | |
36 | 48 |
|
37 | | -## Routes for comparison |
| 49 | +### DVE View Engine Routes |
38 | 50 |
|
39 | | -| Route | Description | Use case | |
40 | | -| -------------- | ---------------------------- | -------------------- | |
41 | | -| `/test` | JSON only, no CPU | Baseline throughput | |
42 | | -| `/test-cpu` | 50k sqrt loop on main thread | Main-thread CPU cost | |
43 | | -| `/test-worker` | Same loop in worker pool | Worker offload cost | |
| 51 | +| Route | What it renders | Why it exists | |
| 52 | +| ---------------------- | ------------------------------- | ---------------------------- | |
| 53 | +| `/test-view` | Simple variable render | DVE baseline | |
| 54 | +| `/test-view-each-meta` | `each` block with metadata | Loop + expression evaluation | |
| 55 | +| `/test-view-include` | Include + partial | Composition overhead | |
| 56 | +| `/test-view-expr` | Optional chaining + expressions | Expression parser/evaluator | |
44 | 57 |
|
45 | | -Run autocannon against each route (in another terminal): |
| 58 | +## Commands (Copy/Paste) |
46 | 59 |
|
47 | 60 | ```bash |
48 | | -# Baseline (no CPU) |
| 61 | +# JSON Baseline |
49 | 62 | npx autocannon http://localhost:8000/test -c 500 -p 10 -d 30 |
50 | 63 |
|
51 | | -# CPU on main thread (blocks event loop) |
| 64 | +# CPU On Main Thread |
52 | 65 | npx autocannon http://localhost:8000/test-cpu -c 500 -p 10 -d 30 |
53 | 66 |
|
54 | | -# CPU in worker (non-blocking) |
| 67 | +# CPU In Worker (Requires benchmark/main-worker.ts) |
55 | 68 | npx autocannon http://localhost:8000/test-worker -c 500 -p 10 -d 30 |
| 69 | + |
| 70 | +# DVE Views |
| 71 | +npx autocannon http://localhost:8000/test-view -c 500 -p 10 -d 30 |
| 72 | +npx autocannon http://localhost:8000/test-view-each-meta -c 500 -p 10 -d 30 |
| 73 | +npx autocannon http://localhost:8000/test-view-include -c 500 -p 10 -d 30 |
| 74 | +npx autocannon http://localhost:8000/test-view-expr -c 500 -p 10 -d 30 |
56 | 75 | ``` |
57 | 76 |
|
58 | | -## Latest benchmark results (30s, 500 conn, pipelining 10) |
| 77 | +## Latest Results (30s, 500 Conn, Pipelining 10) |
59 | 78 |
|
60 | | -**Non-worker** — `main.ts`: |
| 79 | +### JSON + CPU (Example) |
61 | 80 |
|
62 | | -| Route | Test 1 | Test 2 | Test 3 | Req/Sec (avg) | Latency (avg) | Total (avg) | |
63 | | -| -------------- | ------- | ------- | ------- | ------------- | ------------- | ----------- | |
64 | | -| `/test` | 175,343 | 170,567 | 164,515 | 170,142 | 29 ms | 5109k | |
65 | | -| `/test-cpu` | 25,009 | 24,949 | 24,927 | 24,962 | 199 ms | 754k | |
66 | | -| `/test-worker` | 150,151 | 148,683 | 148,061 | 148,965 | 33 ms | 4474k | |
| 81 | +**Non-worker** — `benchmark/main.ts`: |
67 | 82 |
|
68 | | -`/test-worker` without worker returns 503 (no pool). |
| 83 | +| Route | Test 1 | Test 2 | Test 3 | Req/Sec (avg) | Latency (avg) | Total (avg) | |
| 84 | +| ----------- | ------- | ------- | ------- | ------------- | ------------- | ----------- | |
| 85 | +| `/test` | 175,343 | 170,567 | 164,515 | 170,142 | 29 ms | 5109k | |
| 86 | +| `/test-cpu` | 25,009 | 24,949 | 24,927 | 24,962 | 199 ms | 754k | |
69 | 87 |
|
70 | | -**With worker** — `main-worker.ts` (worker pool, poolSize 4). |
| 88 | +**Worker-enabled** — `benchmark/main-worker.ts`: |
71 | 89 |
|
72 | 90 | | Route | Test 1 | Test 2 | Test 3 | Req/Sec (avg) | Latency (avg) | Total (avg) | |
73 | 91 | | -------------- | ------- | ------- | ------- | ------------- | ------------- | ----------- | |
74 | 92 | | `/test` | 174,259 | 178,487 | 170,834 | 174,527 | 28 ms | 5.24M | |
75 | 93 | | `/test-cpu` | 25,133 | 24,833 | 24,773 | 24,912 | 199 ms | 752k | |
76 | 94 | | `/test-worker` | 69,265 | 69,063 | 68,810 | 69,046 | 72 ms | 2076k | |
77 | 95 |
|
78 | | -**Conclusion (test-cpu vs test-worker).** Both routes run the same CPU-bound workload (50k sqrt loop). `/test-cpu` runs it on the main thread and blocks the event loop (~25k req/s, ~199 ms). `/test-worker` offloads the work to a worker pool (~69k req/s, ~72 ms). For CPU-bound tasks, using the worker yields roughly 2.8× higher throughput and ~2.8× lower latency because the main thread stays free to accept and dispatch requests. |
| 96 | +Takeaway: `/test-cpu` blocks the event loop; `/test-worker` moves the same work off-thread. |
79 | 97 |
|
80 | | -## Test Behavior (baseline) |
| 98 | +### Views (DVE Rendering Baseline) |
| 99 | + |
| 100 | +| Route | Test 1 | Test 2 | Test 3 | Req/Sec (avg) | Latency (avg) | Total (avg) | |
| 101 | +| ---------------------- | ------- | ------- | ------- | ------------- | ------------- | ----------- | |
| 102 | +| `/test-view` | 129,063 | 124,978 | 120,516 | 124,852 | 39.77 ms | 3.75M | |
| 103 | +| `/test-view-each-meta` | 10,406 | 10,381 | 10,073 | 10,287 | 482.13 ms | 313k | |
| 104 | +| `/test-view-include` | 112,115 | 109,558 | 101,886 | 107,853 | 46.02 ms | 3.24M | |
| 105 | +| `/test-view-expr` | 96,328 | 94,207 | 82,094 | 90,876 | 54.84 ms | 2.73M | |
| 106 | + |
| 107 | +## Files |
81 | 108 |
|
82 | | -- **Method:** GET |
83 | | -- **Route:** `/test` |
84 | | -- **Response:** `{ "hello": "world!" }` (JSON) |
85 | | -- **File-based routing:** `benchmark/routes/test.ts` → pattern `/test` |
86 | | -- **API:** `Context` + `ctx.send.json()` |
| 109 | +- `benchmark/main.ts`: server entry (non-worker) |
| 110 | +- `benchmark/main-worker.ts`: server entry (worker-enabled) |
| 111 | +- `benchmark/routes/*.ts`: route handlers |
| 112 | +- `benchmark/views/*.dve`: DVE templates |
0 commit comments