Skip to content

Commit cebaaaa

Browse files
authored
Merge pull request #714 from praisethemoon/sch/typev-more-bench
typev: add baseline, json and limited-conn tests
2 parents 6cdda38 + 9980940 commit cebaaaa

25 files changed

Lines changed: 1679 additions & 54 deletions

File tree

frameworks/typev/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ WORKDIR /app
1212
# typev VM — binary + FFI plugins (json, stdcore, stdsocket, ...). Pre-built
1313
# (-O3 -march=x86-64-v3, static liburing) and fetched from object storage
1414
RUN wget -q -O /tmp/typev.zip \
15-
https://typev.s3.fr-par.scw.cloud/typev-16-05-2026.zip && \
15+
https://typev.s3.fr-par.scw.cloud/typev-17-05-2026.zip && \
1616
unzip -q /tmp/typev.zip -d /app && \
1717
rm /tmp/typev.zip
1818

frameworks/typev/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ Type-V is a bytecode-interpreting concurrent VM, using io_uring socket
44
I/O. The benchmark server is written in Type-C (typev's own language) and
55
compiled ahead of time to bytecode that the VM runs.
66

7-
- **Tier:** tuned
8-
- **Profile:** pipelined
7+
- **Tier:** engine
8+
- **Profiles:** pipelined, baseline, limited-conn, json
99

1010
## Layout
1111

@@ -14,7 +14,7 @@ compiled ahead of time to bytecode that the VM runs.
1414
| `Dockerfile` | Downloads the prebuilt typev VM, runs the server |
1515
| `meta.json` | Framework metadata |
1616
| `bundle/output.tvbc` | The compiled benchmark server |
17-
| `bundle/benchmark-code/` | Benchmark source — Type-C `main.tc` plus the `std.io` / `std.socket` modules it uses |
17+
| `bundle/benchmark-code/` | Benchmark source — a Type-C module: `src/` (server, HTTP parser, response builders) plus the `std.io` / `std.socket` modules it uses |
1818

1919
The typev VM itself (binary + FFI plugins) is not vendored here — the Dockerfile
2020
fetches it from object storage at build time. Only the compiled benchmark and
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// Dataset loading + the json profile response writer.
2+
3+
from std.fs import readText
4+
from response import writeBytes, writeInt, digitCount
5+
6+
// A loaded dataset; prefixes[i] is item i's JSON without its closing '}'.
7+
type Dataset = struct {
8+
prefixes: byte[][],
9+
prices: int[],
10+
quantities: int[],
11+
n: int
12+
}
13+
14+
// JSON structural literals, byte-encoded once.
15+
let local const HDR_JSON: byte[] = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: ".bytes()
16+
let local const J_CONN_KA: byte[] = "\r\nConnection: keep-alive\r\n\r\n".bytes()
17+
let local const J_CONN_CL: byte[] = "\r\nConnection: close\r\n\r\n".bytes()
18+
let local const J_OPEN: byte[] = "{\"items\":[".bytes()
19+
let local const J_TOTAL: byte[] = ",\"total\":".bytes()
20+
let local const J_COUNT: byte[] = "],\"count\":".bytes()
21+
22+
// Decimal integer following `key` in data[start..end); 0 if not found.
23+
local fn fieldInt(data: byte[], start: uint, end: uint, key: byte[]) -> int {
24+
let kl: uint = key.length
25+
if kl == 0u || end < start + kl {
26+
return 0
27+
}
28+
let limit: uint = end - kl
29+
foreach i: uint in start, limit + 1u {
30+
let hit: bool = true
31+
foreach k: uint in 0u, kl {
32+
if (data[i + k] as int) != (key[k] as int) {
33+
hit = false
34+
break
35+
}
36+
}
37+
if hit {
38+
let p: uint = i + kl
39+
while p < end && (data[p] as int) == 32 {
40+
p = p + 1u
41+
}
42+
let v: int = 0
43+
while p < end {
44+
let d: int = data[p] as int
45+
if d < 48 || d > 57 {
46+
break
47+
}
48+
v = v * 10 + (d - 48)
49+
p = p + 1u
50+
}
51+
return v
52+
}
53+
}
54+
return 0
55+
}
56+
57+
// Strip insignificant (out-of-string) whitespace from data[start..end) and
58+
// return the compacted bytes.
59+
local fn compactItem(data: byte[], start: uint, end: uint) -> byte[] {
60+
let out: byte[] = new byte[](end - start)
61+
let w: uint = 0u
62+
let inStr: bool = false
63+
let escaped: bool = false
64+
foreach p: uint in start, end {
65+
let c: int = data[p] as int
66+
if inStr {
67+
out[w] = data[p]
68+
w = w + 1u
69+
if escaped {
70+
// this byte was escaped by a preceding backslash — consume it
71+
escaped = false
72+
} else if c == 92 {
73+
escaped = true
74+
} else if c == 34 {
75+
inStr = false
76+
}
77+
} else {
78+
if c == 34 {
79+
inStr = true
80+
out[w] = data[p]
81+
w = w + 1u
82+
} else if c != 32 && c != 10 && c != 13 && c != 9 {
83+
out[w] = data[p]
84+
w = w + 1u
85+
}
86+
}
87+
}
88+
out.resize(w)
89+
return out
90+
}
91+
92+
// Read the dataset file and split the top-level JSON array into its objects.
93+
fn loadDataset(path: string) -> Dataset {
94+
let ds: Dataset = {prefixes: [], prices: [], quantities: [], n: 0}
95+
let (txt, st) = readText(path)
96+
if st < 0 {
97+
return ds
98+
}
99+
let data: byte[] = txt.bytes()
100+
let len: uint = data.length
101+
let priceKey: byte[] = "\"price\":".bytes()
102+
let qtyKey: byte[] = "\"quantity\":".bytes()
103+
104+
// skip to the opening '['
105+
let p: uint = 0u
106+
while p < len && (data[p] as int) != 91 {
107+
p = p + 1u
108+
}
109+
p = p + 1u
110+
111+
let count: int = 0
112+
while p < len {
113+
// skip whitespace and commas between items
114+
while p < len {
115+
let c: int = data[p] as int
116+
if c == 32 || c == 10 || c == 13 || c == 9 || c == 44 {
117+
p = p + 1u
118+
} else {
119+
break
120+
}
121+
}
122+
if p >= len || (data[p] as int) == 93 {
123+
break
124+
}
125+
// scan to the matching '}', string-aware
126+
let itemStart: uint = p
127+
let depth: int = 0
128+
let inStr: bool = false
129+
while p < len {
130+
let c: int = data[p] as int
131+
if inStr {
132+
if c == 92 {
133+
p = p + 1u
134+
} else if c == 34 {
135+
inStr = false
136+
}
137+
} else {
138+
if c == 34 {
139+
inStr = true
140+
} else if c == 123 {
141+
depth = depth + 1
142+
} else if c == 125 {
143+
depth = depth - 1
144+
}
145+
}
146+
p = p + 1u
147+
if !inStr && depth == 0 {
148+
break
149+
}
150+
}
151+
let itemEnd: uint = p
152+
// prefix = item minus the trailing '}', whitespace-compacted
153+
ds.prefixes.push(compactItem(data, itemStart, itemEnd - 1u))
154+
ds.prices.push(fieldInt(data, itemStart, itemEnd, priceKey))
155+
ds.quantities.push(fieldInt(data, itemStart, itemEnd, qtyKey))
156+
count = count + 1
157+
}
158+
ds.n = count
159+
return ds
160+
}
161+
162+
// Loaded once at module init; the container mounts it at /data/dataset.json.
163+
let const DATASET_PATH: string = "/data/dataset.json"
164+
let const DATASET: Dataset = loadDataset(DATASET_PATH)
165+
166+
// /json/{count}?m=M -> {"items":[...],"count":N}; total = price*quantity*mult.
167+
fn writeJson(dst: byte[], pos: uint, count: int, mult: int, keepAlive: bool) -> uint {
168+
let limit: int = if count > DATASET.n => DATASET.n else count
169+
170+
// pass 1: body length
171+
let bodyLen: uint = J_OPEN.length
172+
foreach i in 0, limit {
173+
if i > 0 {
174+
bodyLen = bodyLen + 1u
175+
}
176+
bodyLen = bodyLen + DATASET.prefixes[i as uint].length
177+
bodyLen = bodyLen + J_TOTAL.length
178+
bodyLen = bodyLen + digitCount(DATASET.prices[i as uint] * DATASET.quantities[i as uint] * mult)
179+
bodyLen = bodyLen + 1u
180+
}
181+
bodyLen = bodyLen + J_COUNT.length
182+
bodyLen = bodyLen + digitCount(limit)
183+
bodyLen = bodyLen + 1u
184+
185+
// headers
186+
let p: uint = writeBytes(dst, pos, HDR_JSON)
187+
p = writeInt(dst, p, bodyLen as int)
188+
if keepAlive {
189+
p = writeBytes(dst, p, J_CONN_KA)
190+
} else {
191+
p = writeBytes(dst, p, J_CONN_CL)
192+
}
193+
194+
// pass 2: body
195+
p = writeBytes(dst, p, J_OPEN)
196+
foreach i in 0, limit {
197+
if i > 0 {
198+
dst[p] = 44 as byte
199+
p = p + 1u
200+
}
201+
p = writeBytes(dst, p, DATASET.prefixes[i as uint])
202+
p = writeBytes(dst, p, J_TOTAL)
203+
p = writeInt(dst, p, DATASET.prices[i as uint] * DATASET.quantities[i as uint] * mult)
204+
dst[p] = 125 as byte
205+
p = p + 1u
206+
}
207+
p = writeBytes(dst, p, J_COUNT)
208+
p = writeInt(dst, p, limit)
209+
dst[p] = 125 as byte
210+
return p + 1u
211+
}

0 commit comments

Comments
 (0)