Source
docs/gen-cheatah/gen_bench_parallel.purr
1
# Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).2
# Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.3
# gen_bench_parallel.purr — the pure-cheatah doc-render benchmark, PARALLELIZED across threads over4
# shared memory.Owners. Same render WORK as gen_bench.purr (so the byte total matches, and the timing5
# is apples-to-apples — the only difference is threading). Showcases four stdlib modules cooperating:6
# parsers.xml — parse each Doxygen namespace XML (the from-scratch cheatah XML reader)7
# memory — ONE pinned Owner holds the read-only sources (coexisting shared read leases); TWO8
# Owners are the byte + function-count accumulators (exclusive write leases). The9
# drain-before-write engine makes both totals EXACT regardless of interleaving.10
# thread — thread.spawn runs the workers; each renders a slice of the modules.11
# regex — count the members that are functions (those with an argument list).12
# The byte + function totals are DETERMINISTIC (independent of thread count / interleaving).13
import io14
import os15
import time16
import string17
import parsers.xml18
import regex19
import memory20
import thread22
fn child_text(d, id, tag) { return parsers.xml.text(d, parsers.xml.find(d, id, tag)) }24
fn esc(s) {25
let r = string.replace(s, "&", "&")26
r = string.replace(r, "<", "<")27
return string.replace(r, ">", ">")28
}30
# Render sources [lo, hi) read from the SHARED owner (one parse pass each): accumulate the rendered31
# byte total AND — via regex — the count of function members, then publish both with exclusive writes.32
# The HTML built here is byte-identical to gen_bench.purr's render(), so the totals line up exactly.33
fn worker(srcs_o : memory.Owner<list<str>>, bytes_o : memory.Owner<int>,34
fns_o : memory.Owner<int>, lo : int, hi : int) {35
let re = regex.compile("\\([^)]*\\)") # each thread owns its DFA cache (no shared regex state)36
let lb = 037
let lf = 038
with srcs_o.rread().acquire() as r { # one shared read lease covers this slice39
let i = lo40
while i < hi {41
let d = parsers.xml.parse(r.read(i))42
let root = parsers.xml.root(d)43
for cd in parsers.xml.iter(d, root, "compounddef") {44
lb = lb + len("<h1><code>" + esc(child_text(d, cd, "compoundname")) + "</code></h1>\n")45
for md in parsers.xml.iter(d, cd, "memberdef") {46
let args = child_text(d, md, "argsstring")47
if regex.search(re, args) { lf = lf + 1 }48
let html = "<section class=\"member\"><span class=\"badge\">"49
html = html + parsers.xml.attr(d, md, "kind") + "</span> <code>"50
html = html + esc(child_text(d, md, "name")) + esc(args)51
html = html + "</code><p>" + esc(child_text(d, md, "briefdescription")) + "</p></section>\n"52
lb = lb + len(html)53
}54
}55
i = i + 156
}57
}58
with bytes_o.rwrite().acquire() as w { w.write(w.read() + lb) }59
with fns_o.rwrite().acquire() as w { w.write(w.read() + lf) }60
}62
fn main() {63
let dir = "docs/xml"64
let srcs: list<str> = []65
for f in os.listdir(dir) {66
if string.startswith(f, "namespacecheatah_") and string.endswith(f, ".xml") {67
srcs.append(io.read_file(os.path.join(dir, f)))68
}69
}70
let n = len(srcs)71
let srcs_o = memory.own(srcs)73
# ONE timed pass per invocation, matching gen_bench.purr and gen_bench.py so74
# gen_bench_compare.purr can interleave all three. Every sample is printed; a best-of-N75
# minimum hides exactly the dispersion a threading claim most needs to show.76
let bytes = 077
let fns = 078
let q = n / 479
let k = 080
while k < 1 {81
let bytes_o = memory.own(0)82
let fns_o = memory.own(0)83
let t0 = time.perf_counter()84
with thread.spawn(worker, srcs_o, bytes_o, fns_o, 0, q) {85
with thread.spawn(worker, srcs_o, bytes_o, fns_o, q, q + q) {86
with thread.spawn(worker, srcs_o, bytes_o, fns_o, q + q, q + q + q) {87
with thread.spawn(worker, srcs_o, bytes_o, fns_o, q + q + q, n) {88
}89
}90
}91
}92
let dt = time.perf_counter() - t093
io.print("sample_ms=" + io.str(dt * 1000.0))94
with bytes_o.rread().acquire() as r { bytes = r.read() }95
with fns_o.rread().acquire() as r { fns = r.read() }96
k = k + 197
}98
io.print("cheatah-parallel files=" + io.str(n) + " out_bytes=" + io.str(bytes) + " fns=" + io.str(fns))99
}101
main()