Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 34 additions & 40 deletions frameworks/vanilla-io_uring/main.v
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ mut:
// json-comp cache: the gzipped response for a given (count, m) is fully
// deterministic and gzip dominates the cost, so compress once and reuse.
// Key = (count << 32) | m. The benchmark hits only a few pairs, so it's tiny.
gz_cache map[u64][]u8 // json-comp: precomputed at boot, READ-ONLY during serving (lock-free reads)
gz_cache map[u64][]u8
gz_mu &sync.RwMutex = unsafe { nil }
}

struct CrudCreate {
Expand Down Expand Up @@ -385,39 +386,45 @@ fn (sh &Shared) write_json_response(mut out []u8, count int, m i64) {
ws(mut out, '}')
}

// gz_response builds the COMPLETE gzipped /json response for (count, m). Pure
// function of the shared dataset (deterministic), so it is precomputed once at
// boot (see main) and also serves the cold path for an unexpected param.
fn gz_response(sh &Shared, count int, m i64) []u8 {
// write_json_gzip is the json-comp path. The gzipped response for a given
// (count, m) is fully deterministic and gzip CPU dominates the cost, so we cache
// the COMPLETE response bytes and just append the cached copy on a hit — no
// rebuild, no recompress. Compressing once instead of per-request is the whole
// win for json-comp (the profile is compression-bound, not allocation-bound).
//
// Kept LAZY, not precomputed at boot: precomputing the full (count x m) grid would
// compress ~800 keys vs the ~8 the profile sends. On the `-gc none` epoll sibling
// that leaked ~135 MiB via gzip.compress scratch (vlang/v#27606); this io_uring
// binary is default-GC so it would not leak, but lazy keeps both entries identical
// and the shared lock is not a bottleneck (the json-comp@16384 collapse is thread
// oversubscription from the co-hosted json-tls listener, enghitalo/vanilla#89).
fn (mut sh Shared) write_json_gzip(mut out []u8, count int, m i64) {
key := (u64(u32(count)) << 32) | u64(u32(m))
sh.gz_mu.@rlock()
cached := sh.gz_cache[key] or { []u8{} }
sh.gz_mu.runlock()
if cached.len > 0 {
out << cached
return
}
body := sh.json_body(count, m)
gz := gzip.compress(body.bytes()) or { return []u8{} }
gz := gzip.compress(body.bytes()) or {
write_resp(mut out, 'application/json', body)
return
}
mut resp := []u8{cap: gz.len + 128}
ws(mut resp,
'HTTP/1.1 200 OK\r\nServer: vanilla\r\nContent-Encoding: gzip\r\nContent-Type: application/json\r\nContent-Length: ')
wi(mut resp, i64(gz.len))
ws(mut resp, '\r\nConnection: keep-alive\r\n\r\n')
unsafe { resp.push_many(gz.data, gz.len) }
return resp
}

// write_json_gzip is the json-comp path. The gzipped response per (count, m) is
// deterministic, so the cache is fully precomputed at boot and is READ-ONLY during
// serving — the hot path is a lock-free map read (no per-request shared RwMutex,
// which contended across all workers and collapsed io_uring json-comp @16384).
fn (sh &Shared) write_json_gzip(mut out []u8, count int, m i64) {
key := (u64(u32(count)) << 32) | u64(u32(m))
if cached := sh.gz_cache[key] {
out << cached
return
}
// Param outside the precomputed grid (not sent by the benchmark): build and
// send WITHOUT caching, so the hot path stays write-free and lock-free.
resp := gz_response(sh, count, m)
if resp.len > 0 {
out << resp
} else {
write_resp(mut out, 'application/json', sh.json_body(count, m))
// Store it (bounded so a flood of distinct m values can't grow it without limit).
sh.gz_mu.@lock()
if sh.gz_cache.len < 1024 {
sh.gz_cache[key] = resp
}
sh.gz_mu.unlock()
out << resp
}

// json_body builds just the /json body string (used for the gzip path).
Expand Down Expand Up @@ -762,20 +769,7 @@ fn main() {
cache: map[int]string{}
cache_mu: sync.new_rwmutex()
gz_cache: map[u64][]u8{}
}

// Precompute the json-comp (gzip) responses at boot so write_json_gzip reads the
// cache LOCK-FREE during serving (no per-request shared RwMutex — that contended
// across workers and collapsed io_uring json-comp @16384 conns). The profile's
// params are bounded; cover count 1..min(dataset,64) x m 1..16.
gz_cap_count := if dataset.len < 64 { dataset.len } else { 64 }
for c in 1 .. gz_cap_count + 1 {
for mm in 1 .. 17 {
r := gz_response(sh, c, i64(mm))
if r.len > 0 {
sh.gz_cache[(u64(u32(c)) << 32) | u64(u32(mm))] = r
}
}
gz_mu: sync.new_rwmutex()
}

// ── json-tls profile: /json over HTTPS on :8081 via the epoll + kTLS backend ──
Expand Down
24 changes: 12 additions & 12 deletions site/data/api-16-1024.json
Original file line number Diff line number Diff line change
Expand Up @@ -1507,28 +1507,28 @@
{
"framework": "vanilla-io_uring",
"language": "V",
"rps": 29136,
"avg_latency": "33.91ms",
"p99_latency": "270.00ms",
"cpu": "1499.7%",
"memory": "2.3GiB",
"rps": 28450,
"avg_latency": "34.72ms",
"p99_latency": "268.30ms",
"cpu": "1459.0%",
"memory": "2.1GiB",
"connections": 1024,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "146.61MB/s",
"input_bw": "1.64MB/s",
"reconnects": 87278,
"status_2xx": 437050,
"bandwidth": "143.12MB/s",
"input_bw": "1.60MB/s",
"reconnects": 85218,
"status_2xx": 426754,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0,
"tpl_baseline": 163773,
"tpl_json": 163787,
"tpl_baseline": 160037,
"tpl_json": 160077,
"tpl_db": 0,
"tpl_upload": 0,
"tpl_static": 0,
"tpl_async_db": 109490
"tpl_async_db": 106640
},
{
"framework": "workerman",
Expand Down
22 changes: 11 additions & 11 deletions site/data/api-4-256.json
Original file line number Diff line number Diff line change
Expand Up @@ -1507,28 +1507,28 @@
{
"framework": "vanilla-io_uring",
"language": "V",
"rps": 28454,
"avg_latency": "7.83ms",
"p99_latency": "54.30ms",
"cpu": "360.5%",
"memory": "2.1GiB",
"rps": 28434,
"avg_latency": "7.65ms",
"p99_latency": "57.70ms",
"cpu": "356.8%",
"memory": "2.0GiB",
"connections": 256,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "143.08MB/s",
"bandwidth": "143.02MB/s",
"input_bw": "1.60MB/s",
"reconnects": 85343,
"status_2xx": 426819,
"reconnects": 85290,
"status_2xx": 426516,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0,
"tpl_baseline": 160147,
"tpl_json": 159929,
"tpl_baseline": 159969,
"tpl_json": 159971,
"tpl_db": 0,
"tpl_upload": 0,
"tpl_static": 0,
"tpl_async_db": 106741
"tpl_async_db": 106576
},
{
"framework": "workerman",
Expand Down
18 changes: 9 additions & 9 deletions site/data/async-db-1024.json
Original file line number Diff line number Diff line change
Expand Up @@ -1259,19 +1259,19 @@
{
"framework": "vanilla-io_uring",
"language": "V",
"rps": 10645,
"avg_latency": "95.83ms",
"p99_latency": "405.90ms",
"cpu": "4906.4%",
"memory": "1.9GiB",
"rps": 11025,
"avg_latency": "92.40ms",
"p99_latency": "385.40ms",
"cpu": "5086.3%",
"memory": "1.8GiB",
"connections": 1024,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "40.86MB/s",
"input_bw": "727.69KB/s",
"reconnects": 3851,
"status_2xx": 106453,
"bandwidth": "42.50MB/s",
"input_bw": "753.66KB/s",
"reconnects": 3988,
"status_2xx": 110258,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0
Expand Down
14 changes: 7 additions & 7 deletions site/data/baseline-4096.json
Original file line number Diff line number Diff line change
Expand Up @@ -1751,19 +1751,19 @@
{
"framework": "vanilla-io_uring",
"language": "V",
"rps": 3815248,
"avg_latency": "1.07ms",
"p99_latency": "6.97ms",
"cpu": "5989.3%",
"rps": 3655431,
"avg_latency": "1.12ms",
"p99_latency": "7.14ms",
"cpu": "5411.7%",
"memory": "1.5GiB",
"connections": 4096,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "389.16MB/s",
"input_bw": "294.72MB/s",
"bandwidth": "372.79MB/s",
"input_bw": "282.37MB/s",
"reconnects": 0,
"status_2xx": 19076241,
"status_2xx": 18277158,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0
Expand Down
14 changes: 7 additions & 7 deletions site/data/baseline-512.json
Original file line number Diff line number Diff line change
Expand Up @@ -1751,19 +1751,19 @@
{
"framework": "vanilla-io_uring",
"language": "V",
"rps": 3326849,
"avg_latency": "153us",
"p99_latency": "1.41ms",
"cpu": "5576.7%",
"rps": 3275759,
"avg_latency": "155us",
"p99_latency": "1.43ms",
"cpu": "6403.6%",
"memory": "1.5GiB",
"connections": 512,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "339.35MB/s",
"input_bw": "256.99MB/s",
"bandwidth": "334.17MB/s",
"input_bw": "253.04MB/s",
"reconnects": 0,
"status_2xx": 16634248,
"status_2xx": 16378798,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0
Expand Down
18 changes: 9 additions & 9 deletions site/data/crud-4096.json
Original file line number Diff line number Diff line change
Expand Up @@ -522,19 +522,19 @@
{
"framework": "vanilla-io_uring",
"language": "V",
"rps": 221360,
"avg_latency": "18.47ms",
"p99_latency": "177.90ms",
"cpu": "1445.6%",
"memory": "1.7GiB",
"rps": 231065,
"avg_latency": "17.69ms",
"p99_latency": "178.50ms",
"cpu": "1458.0%",
"memory": "1.9GiB",
"connections": 4096,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "69.78MB/s",
"input_bw": "19.00MB/s",
"reconnects": 14598,
"status_2xx": 3320411,
"bandwidth": "73.10MB/s",
"input_bw": "19.83MB/s",
"reconnects": 15393,
"status_2xx": 3465984,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0
Expand Down
14 changes: 7 additions & 7 deletions site/data/fortunes-1024.json
Original file line number Diff line number Diff line change
Expand Up @@ -192,18 +192,18 @@
{
"framework": "vanilla-io_uring",
"language": "V",
"rps": 70,
"avg_latency": "2.27s",
"p99_latency": "5.00s",
"cpu": "1055.2%",
"memory": "1.4GiB",
"rps": 37,
"avg_latency": "2.96s",
"p99_latency": "4.99s",
"cpu": "966.9%",
"memory": "1.7GiB",
"connections": 1024,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "1.66MB/s",
"bandwidth": "902.86KB/s",
"reconnects": 0,
"status_2xx": 350,
"status_2xx": 186,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0
Expand Down
14 changes: 7 additions & 7 deletions site/data/json-4096.json
Original file line number Diff line number Diff line change
Expand Up @@ -1479,19 +1479,19 @@
{
"framework": "vanilla-io_uring",
"language": "V",
"rps": 2407630,
"rps": 2415906,
"avg_latency": "881us",
"p99_latency": "7.41ms",
"cpu": "6373.4%",
"p99_latency": "7.34ms",
"cpu": "6379.9%",
"memory": "1.4GiB",
"connections": 4096,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "8.15GB/s",
"input_bw": "114.80MB/s",
"reconnects": 481458,
"status_2xx": 12038150,
"bandwidth": "8.18GB/s",
"input_bw": "115.20MB/s",
"reconnects": 483317,
"status_2xx": 12079530,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0
Expand Down
16 changes: 8 additions & 8 deletions site/data/json-comp-16384.json
Original file line number Diff line number Diff line change
Expand Up @@ -1222,19 +1222,19 @@
{
"framework": "vanilla-io_uring",
"language": "V",
"rps": 159300,
"avg_latency": "43.02ms",
"p99_latency": "1.48s",
"cpu": "437.8%",
"rps": 155961,
"avg_latency": "47.80ms",
"p99_latency": "1.79s",
"cpu": "484.9%",
"memory": "2.0GiB",
"connections": 16384,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "260.34MB/s",
"input_bw": "11.85MB/s",
"reconnects": 30427,
"status_2xx": 796504,
"bandwidth": "254.75MB/s",
"input_bw": "11.60MB/s",
"reconnects": 29439,
"status_2xx": 779805,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0
Expand Down
Loading