$ k6 run bench/workload.js
How fast is a verified web server?
Rust (h5i-app), Go, TypeScript and Python serving the same APIs and PostgreSQL data. Tested with k6 at 1–256 concurrent clients after API conformance checks.
Overview
Requests/s at 16 clients. Darker shading means faster within a row. – = unmeasured.
django-cart execution-strategy experiments · SQL, request batching and line scaling
Supplemental measurements compare execution strategies. Their original implementation names and measurement conditions are preserved.
GPU search
Linkding · 2026-10-17. The same server with CPU or GPU search over a resident index.
Full HTTP requests, including authentication, SQL, pagination and JSON. At 16 clients, GPU search is 6.94× faster for simple search and 12.51× for complex search than the resident CPU version.
| Scenario | Clients | Optimized SQL | CPU scan | GPU scan | Upstream 1.47.0 | GPU / CPU |
|---|---|---|---|---|---|---|
search | 1 | 4.72 | 34.69 | 65.26 | 4.95 | 1.88× |
search | 16 | 19.52 | 96.32 | 668.96 | 13.53 | 6.94× |
search_complex | 1 | 2.26 | 10.72 | 48.29 | 1.86 | 4.50× |
search_complex | 16 | 5.00 | 15.26 | 190.91 | 4.15 | 12.51× |
mixed | 1 | 15.89 | 8.76 | 7.37 | 9.88 | 0.84× |
mixed | 16 | 84.31 | 32.24 | 35.67 | 34.00 | 1.11× |
Optimized SQL is faster for mixed traffic. At one client, GPU mixed throughput falls below CPU. The CPU scan here uses a resident index; it is a different implementation from the normal h5i engine.
Run conditions
- 100,000 bookmarks, 2,000 tags, 50 users. Simple and synthetic eight-term searches each match 1,000 active bookmarks; the first page returns 20.
- Dual EPYC 7502, RTX 3090. Four concurrent variants, each with 4 server, 4 PostgreSQL and 4 client CPUs. Shared host; clocks and NUMA placement vary.
- Three repetitions, fresh database/server, 10 s warmup, 30 s measurement. Index initialization is in warmup; mixed traffic includes rebuilding after writes. 128,022 measured requests; zero failures. Cost, power and cold start were not measured.
Report · Summary CSV · Per-point CSV
Complex search measured separately on 2026-10-19: h5i engine, Go, Python and TypeScript.
GPU policies
Conduit · 2026-10-18. The same server with CPU or GPU policy decisions.
CUDA handles eight authorization and state decisions. HTTP, SQL and serialization use the same code. The CPU kernel has Lean proofs; GPU behavior is tested against CPU results. List, slug and login are controls that do not use GPU decisions.
| Scenario | Clients | CPU req/s | GPU req/s | GPU / CPU | CPU p99 ms | GPU p99 ms |
|---|---|---|---|---|---|---|
list | 1 | 83.37 | 82.83 | 0.99× | 32.69 | 32.65 |
list | 16 | 766.32 | 769.67 | 1.00× | 78.78 | 78.82 |
slug | 1 | 1,110.16 | 1,118.36 | 1.01× | 0.96 | 1.00 |
slug | 16 | 13,560.10 | 14,174.03 | 1.05× | 8.89 | 8.55 |
feed | 1 | 187.76 | 179.55 | 0.96× | 5.80 | 6.10 |
feed | 16 | 2,194.92 | 2,201.72 | 1.00× | 14.30 | 13.99 |
login | 1 | 3.85 | 3.83 | 1.00× | 260.47 | 261.96 |
login | 16 | 15.45 | 15.41 | 1.00× | 1,113.43 | 1,104.02 |
create | 1 | 196.21 | 190.83 | 0.97× | 5.64 | 5.78 |
create | 16 | 216.31 | 209.85 | 0.97× | 363.18 | 367.29 |
favorite | 1 | 504.53 | 439.16 | 0.87× | 2.63 | 3.15 |
favorite | 16 | 4,457.18 | 4,725.08 | 1.06× | 19.18 | 17.90 |
mixed | 1 | 121.54 | 123.87 | 1.02× | 35.13 | 33.75 |
mixed | 16 | 1,001.07 | 982.93 | 0.98× | 68.70 | 71.16 |
No consistent GPU speedup. Mixed throughput is 0.98× CPU at 16 clients; GPU host RSS is 93–98 MiB versus 7–11 MiB. The 1.06× favorite result is within observed variation. CPU remains the default.
Run conditions
- 100 users, 10,000 articles, 10,000 comments, 1,000 follows, 5,000 favorites. Three repetitions, fresh database/server, 10 s warmup, 30 s measurement; startup excluded.
- Dual EPYC 7502, two RTX 3090s. Two CPU/GPU pairs split the scenarios; each variant has 4 server, 4 PostgreSQL and 4 client CPUs. Shared host; clocks and NUMA placement vary.
- 4,467,960 measured requests; zero failures. GPU execution was logged with no runtime CPU fallback. Paired runs cover 1 and 16 clients; cost and power were not measured.
GPU cart pricing
django-cart 3.1.1 · 2026-10-19. Recalculate already-loaded cart snapshots.
Compare Rust, Go, Node.js and CUDA using identical cart snapshots. Select lines per cart to see how batch size changes the result. Milliseconds per complete batch, lower is faster; median of three repetition means. Hover over a timing for its measured range.
GPU resident reuses unchanged input; upload each transfers all snapshots every call. Both download all totals. A CPU could cache unchanged totals too; useful residency needs changing prices or discount scenarios, including synchronization costs. See candidate workloads and evaluation requirements. This is an offline calculation microbenchmark. For HTTP request coalescing and SQL query batching, see django-cart totals and the 32–2,048 lines/cart comparison above. Session handling, database aggregation and checkout transactions are excluded. This does not establish a speedup over a Django deployment.
Run conditions and correctness
- EPYC 7502, CPUs 64–79, RTX 3090 device 0, CUDA 12.6. Eight warmup passes; three repetitions, each averaging 100 iterations below 4,096 carts and 10 otherwise; shared host and uncontrolled clocks/NUMA placement.
- GPU snapshots are transposed on upload for contiguous reads; upload-each timing includes that conversion. The same integer pricing source runs on CPU and CUDA using h5i's NVVM launch shell. CPU 16 uses persistent Rayon workers. GPU resident timing includes launch, synchronization, result allocation, download and error validation.
- 4,146 cases agree with the pinned upstream's Decimal methods; 49 exercise its ORM aggregate. Rust, Go, Node.js and CUDA agree with this corpus. CPU/GPU checks also cover malformed snapshots and batch boundaries. This pricing kernel has no Lean proof.
- None, percentage and fixed discounts, final half-up rounding, default zero tax/shipping. Fixed-width snapshots reserve 32 lines; no mutations or live cache invalidation are measured.
Contract and reproduction · All batch sizes · Upstream source
Resident cart updates across languages
Go, JavaScript, Python, NumPy, Rust and GPU: the same changing input and every total returned.
Run conditions and correctness
Every path retains rows, subtotals and totals between batches. Canonical commands contain explicit u64 query IDs and four u64 fields per replacement. Timings include applying updates, pricing, worker dispatch/join and a complete freshly owned output. GPU paths also include encoding where applicable, upload, synchronization and full download. All returned values are checked exactly outside timing. No SQL/HTTP is included.
One or four persistent computation workers; Python 4 uses fork processes, JavaScript 4 uses worker threads, Go 4 uses goroutines, Rust 4 uses Rayon. NumPy is a separate native vectorized implementation with native thread counts fixed to one. All modes share the same four-core allocation (CPUs 64–67), with GC enabled. CPU timings are collected without concurrent benchmark processes. If GPU references from the prior run are shown, their status is explicit above and their ratios are not contemporary speedups. JavaScript uses exact Float64 integer subtotals and quotient/remainder discount rounding. CPython loops use Python integers with u64 retained storage; NumPy is used for untimed input validation in every Python mode.
Initial placement, loading, worker creation and validation are excluded. Python imports initial subtotal caches from fixtures, computes initial totals itself, and independently rescans subtotals for every upstream gate; other languages construct their initial caches. Initialization timings are not comparable cache-construction costs. Three repetitions, eight changing warmups and 50 changing timed rounds; RTX 3090 device 0. Shared host; clocks, NUMA and cache placement are uncontrolled. This is a resident calculation/update benchmark, not a Django deployment speedup.
Cart update and GPU tuning experiments · 2026-10-19–20
Resident update sweeps, transfer diagnostics and compact-buffer comparisons. Calculation timings exclude HTTP and SQL.
Resident carts with updates
CPU and GPU retain the same 4,096-cart population. Apply line replacements, then query current totals.
Microseconds per complete update-and-query batch, lower is faster. GPU includes command upload, mutation/query kernels, synchronization and full result download. CPU and GPU delta maintain subtotals and cached totals; this baseline matters because simple sums do not require full rescans. This is a calculation experiment: database commits, CDC, HTTP and cross-process coherence are excluded.
These initial results use a single-thread CPU delta baseline. See larger cart update batches for grouped single/four-thread CPU delta comparisons.
Run conditions and correctness
Three repetitions × 100 changing rounds after eight warmups. Every warmup and timed output is checked against a separate full-rescan state machine; the very next query must observe replacements. CPU delta is single-threaded; the four-worker CPU path rescans all lines. Parallel CPU delta is not part of this initial run. One/four CPU workers share CPUs 64–67; RTX 3090 device 0. Variant order rotates/reverses; clocks, NUMA, host activity and caches are uncontrolled. Initial placement is separate and includes subtotal initialization for both delta paths. p50/p99 are averaged iteration percentiles, not pooled HTTP percentiles.
Larger cart update batches
4,096–65,536 resident carts, including single/four-thread incremental CPU baselines.
Every cart is queried after changing 1, 16 or 64 of its lines. CPU and GPU retain the same data and cached subtotals. The comparison uses the fastest measured implementation on each side; above 1 favors GPU. GPU timing includes fresh command upload, kernels, synchronization and complete result download.
Measured update transfer, kernel and result phases for four selected conditions.
Run conditions and correctness
Seven implementations × 18 shapes × three repetitions; 50 changing measured rounds after eight warmups. Every output is checked exactly against an independent full-rescan state machine. Grouped CPU delta processes all changes for a cart together, with either one thread or four persistent Rayon workers. CPUs 64–67, RTX 3090 device 0; shared host, uncontrolled clocks/NUMA/cache. Initial placement is separate. Iteration percentiles are averaged across repetitions; with 50 rounds p99 is the maximum round. These are Rust calculation measurements, excluding HTTP, database commits, CDC delivery and durable cache coherence.
Cart update GPU phases
Separate changing-trace replay of the 1.79× condition and three larger update batches.
Compact input and pinned-buffer comparison, including packing costs.
Run conditions and correctness
The one-update delta host replay and small-shape delta event replay contain large upload outliers; all rounds are retained. Host columns are disjoint wall-time intervals. Upload/download include pageable staging and driver overhead; they are not isolated PCIe times. Sync includes remaining kernel execution and waiting overhead. Device event intervals come from a separate replay, overlap enqueue/sync, and must not be added to the host columns. Delta fuses update and total gathering in one kernel. All modes replay the same 58 changing rounds from reset state; three repetitions, eight warmups. Initialization is excluded. Shared host, uncontrolled clocks/NUMA/cache.
Compact cart updates and pinned buffers
32-byte versus 8-byte replacements, with pageable or reusable page-locked host staging.
Run conditions and correctness
All paths consume the same canonical commands and return a fresh owned vector of every queried total. Encoding and staging copies are timed. A compact replacement packs slot (11 bits), price (27) and quantity (10); cart ID comes from the validated round-robin query order. Query IDs remain explicit. This format requires that routing contract. Compact encoders run on one or four workers; encode4 uses the same persistent Rayon pool and four-core budget as CPU4. Pinned paths copy/encode into reusable locked input, queue upload/kernels/full download in one stream, wait, then copy locked output into an owned vector. There is no overlap across batches. Initialization is separate.
Plain runs drive comparisons. Event runs are separate diagnostics: upload, kernels and download use the same stream. Pageable upload intervals include possible host submission gaps; they do not isolate DMA. Event readout is included in the diagnostic total. Three repetitions, 50 changing rounds after eight warmups; CPUs 64–67 and RTX 3090 device 0. Shared host, uncontrolled clocks/NUMA/cache. Controls retain the original host calls and kernel source, compiled into a separate PTX. HTTP, database commits and CDC are excluded.
Linkding tuning experiments · 2026-10-18
Separate before/after runs; these results are not included in the overview.
Linkding search initialization
Post-seed ANALYZE · 2026-10-18.
| Scenario | Clients | Before | After | After / before |
|---|---|---|---|---|
get | 1 | 480.12 | 462.90 | 0.96× |
get | 64 | 11407.44 | 11503.83 | 1.01× |
list | 1 | 31.72 | 21.60 | 0.68× |
list | 64 | 136.91 | 120.64 | 0.88× |
search | 1 | 0.97 | 1.87 | 1.93× |
search | 64 | 4.13 | 7.54 | 1.83× |
Search improves 1.83× at 64 clients; list throughput falls to 0.88×. Instrumented runs: 5 s warmup, 15 s measurement, three repetitions. Search reads 287,002 physical rows per request.
Linkding retrieval, creation and search
Combined bookmark/tag retrieval and scalar SQL · 2026-10-18.
| Route | Clients | Before | After | Ratio | Reference | Reference req/s |
|---|---|---|---|---|---|---|
| Optimized get | 1 | 460.12 | 694.76 | 1.51× | h5i | 475.71 |
| Optimized get | 64 | 4353.98 | 6573.41 | 1.51× | h5i | 8582.49 |
| Engine create | 1 | 48.54 | 174.66 | 3.60× | Go | 152.26 |
| Engine create | 64 | 29.88 | 224.81 | 7.52× | Go | 3525.29 |
| Engine search | 1 | 1.86 | 2.36 | 1.27× | Go | 4.73 |
| Engine search | 64 | 6.68 | 10.24 | 1.53× | Go | 19.21 |
Get improves 1.51×. Engine creation and search improve, but Go stays faster at 64 clients. No timing probes; three repetitions with CPU/database slot rotation. Read SQL is differential-tested; the kernel and its Lean proofs are unchanged.
Single-bookmark retrieval
Reuse the authorized bookmark row · 2026-10-18.
| Clients | Before | After | Ratio | Go |
|---|---|---|---|---|
| 1 | 331.60 | 399.48 | 1.20× | 402.78 |
| 64 | 2866.83 | 3676.61 | 1.28× | 6450.85 |
Get improves 1.20–1.28×. Go was measured concurrently on a shared host with slot rotation. Three repetitions, 5 s warmup and 15 s measurement; zero failures. The earlier sequential run regressed at 64 clients; see the report.
Read SQL modes
Membership versus scalar subqueries · 2026-10-18.
| Clients | Membership | Scalar when unique | Ratio |
|---|---|---|---|
| 1 | 31.20 | 47.86 | 1.53× |
| 64 | 154.11 | 259.12 | 1.68× |
Scalar mode improves list throughput 1.53–1.68× at fixed offset 100. Timing probes enabled; three repetitions. Both modes return 20 results and an exact total of 95,000. The earlier rotating-offset regression remains unresolved; IN remains the default.
Unique-membership SQL
Scalar subqueries for primary-key membership · 2026-10-18.
| Scenario | Clients | Before | After | After / before |
|---|---|---|---|---|
get | 1 | 480.17 | 490.78 | 1.02× |
get | 64 | 11443.34 | 11180.28 | 0.98× |
list | 1 | 21.81 | 22.23 | 1.02× |
list | 64 | 120.26 | 107.66 | 0.90× |
search | 1 | 1.87 | 2.40 | 1.28× |
search | 64 | 7.70 | 11.81 | 1.53× |
Search improves 1.28–1.53×; list throughput falls to 0.90× at 64 clients. Instrumented runs: 5 s warmup, 15 s measurement, three repetitions. Read SQL is differential-tested; application decisions and read/count plans are unchanged.
Method
Overview runs: fresh seed, 10 s warmup, 30 s measurement, mean of three repetitions. Targeted experiments list their own conditions.
- Servers, PostgreSQL and k6 use separate CPUs. Serial runs give each server 4 cores: 4 workers for Python, Node and Go; 4 Tokio threads for Rust.
- Requests/s includes k6's graceful stop. Latencies are per-run percentiles; RSS is peak memory across the server process tree.
- Login uses bcrypt cost 12, which dominates that scenario.
- Single-host measurements. Scripts and CSV results: performance/.