Skip to content

Commit d35a48d

Browse files
olwangclaude
andcommitted
bench(vm-jit): add deepcopy_read_param kernel (Phase 2 measurement harness)
A read-only nested-struct param passed to a helper in a hot loop — the workload DeepCopy elision targets. Reads go through List.len/List.get so the param never routes through a mutator. Measurement (rss run --vm, flag off vs on) confirmed identical output (parity) but 0% speedup: List.len/get lower to CallTypedIntrinsic, and the elision v1 whitelist conservatively keeps the copy whenever a tainted reg reaches an intrinsic — so collection-read params are never elided. This kernel is the validation harness for Phase 2 v2 (a pure-read-intrinsic classifier), which is where the win actually is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0027b85 commit d35a48d

1 file changed

Lines changed: 68 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
features: local
2+
3+
// Phase 2 measurement kernel: a non-mut nested-struct param passed to a READ-ONLY
4+
// helper in a hot loop. Under the eager scheme `sum_reads` deep-copies all three
5+
// `List<Int>` fields of `Bag` on EVERY call (RegInstr::DeepCopy in the prologue);
6+
// with RSS_VM_ELIDE_DEEPCOPY the copy is elided because `g` is only ever read
7+
// (List.len / List.get / field reads — no mutation, store, or escape). The delta
8+
// between flag-off and flag-on runs is the per-call deep-copy cost this phase
9+
// targets. Reads go through ListGet/ListLen/GetField specifically (the analysis'
10+
// read-only-safe set), so elision fires; nothing routes `g` through an intrinsic,
11+
// a mut arg, an aggregate store, or a return.
12+
13+
fn bench_size(default: Int) -> Int {
14+
let raw = Args.get_or_default(index: 0, default: read String.from_int(value: default))
15+
match String.parse_int(value: read raw) {
16+
Some(value) => {
17+
return value
18+
}
19+
None => {
20+
return default
21+
}
22+
}
23+
}
24+
25+
struct Bag {
26+
a: List<Int>,
27+
b: List<Int>,
28+
c: List<Int>,
29+
}
30+
31+
fn sum_reads(g: read Bag, i: Int) -> Int {
32+
let na = List.len<Int>(list: read g.a)
33+
let nb = List.len<Int>(list: read g.b)
34+
let nc = List.len<Int>(list: read g.c)
35+
let va = List.get<Int>(list: read g.a, index: i % na)
36+
let vb = List.get<Int>(list: read g.b, index: i % nb)
37+
let vc = List.get<Int>(list: read g.c, index: i % nc)
38+
return va + vb + vc
39+
}
40+
41+
fn hot(g: read Bag, limit: Int) -> Int {
42+
let mut i = 0
43+
let mut total = 0
44+
while i < limit {
45+
total = total + sum_reads(g: read g, i: i)
46+
i = i + 1
47+
}
48+
return total
49+
}
50+
51+
fn build_list(n: Int) -> List<Int> {
52+
local xs = List<Int>.new()
53+
let mut k = 0
54+
while k < n {
55+
let v = k * k - k
56+
List.push<Int>(list: mut xs, value: read v)
57+
k = k + 1
58+
}
59+
return xs
60+
}
61+
62+
fn main() -> Unit {
63+
let limit = bench_size(default: 500000)
64+
let bag = Bag(a: build_list(n: 256), b: build_list(n: 256), c: build_list(n: 256))
65+
let result = hot(g: read bag, limit: limit)
66+
Log.write(message: read String.from_int(value: result))
67+
return Unit
68+
}

0 commit comments

Comments
 (0)