arena (25.3b): LOCAL sites allocate from the frame's scratch; dispatch is not an allocation; @frame by property
Behind ludicc --arena (or 'arena on' in the program's package.ludic): the escape analysis runs and a LOCAL site's allocation raises @lp_want for that one call, so it comes from the frame's arena. Two halves in one mmap reservation (R3D_ARENA_MB each, 256 by default), bump-allocated with a 16-byte size header, flipped at each frame mark: a frame's scratch is good through the next frame, then its half is started again (R3D_ARENA_CHECK=1 fills it with 0xDD first). The heap takes over when no frame is running, off the main thread, or past the half's end; lp_free ignores an arena block and lp_realloc copies one out. R3D_ARENA=0 turns it off at run time; the census reports each half's high-water mark. A program that builds text, a list and a record per frame: 29998 heap blocks made and 18002 freed without it, 8 and 8 with it and 544 bytes of scratch a frame, the same output. A dispatch's 'new' fills the queue's kept record (E_NEW.b), so 25.2 no longer counts it and 25.3 treats its fields as kept. '@frame' is keyed by property and field: a 'run' field is a root only in a property that marks it, and 'tick' stays a System's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8dfcccad41
commit
bef0d6fbca
11 changed files with 80823 additions and 78428 deletions
|
|
@ -31,10 +31,14 @@ function fr_phase_frame(ph: pointer) -> bool {
|
|||
return (ph == "Input") or (ph == "FixedUpdate") or (ph == "Update") or (ph == "LateUpdate") or (ph == "Render") or (ph == "Overlay")
|
||||
}
|
||||
|
||||
function fr_is_frame_field(name: pointer) -> bool {
|
||||
# `tick` is a System's, by any name's record; any other field is a frame root only where its own
|
||||
# property says `@frame` (Prop.field), so a step list of loads with a `run` field is not one
|
||||
function fr_is_frame_field(name: pointer) -> bool { return (name == "tick") }
|
||||
function fr_is_frame_prop_field(prop: pointer, name: pointer) -> bool {
|
||||
if (name == "tick") { return true }
|
||||
let key = `{prop}.{name}`
|
||||
var i = 0
|
||||
while i < len(g_frame_fields) { if (g_frame_fields[i] == name) { return true }; i += 1 }
|
||||
while i < len(g_frame_fields) { if (g_frame_fields[i] == key) { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -60,11 +64,11 @@ function fr_mark_root(name: pointer) -> void {
|
|||
function fr_find_roots(n: Node) -> void {
|
||||
if n == null { return }
|
||||
if n.kind == S_ASSIGN and n.a != null and n.a.kind == E_MEMBER and n.a.s != null and fr_is_frame_field(n.a.s) and n.b != null and n.b.kind == E_FNREF { fr_mark_root(n.b.s) }
|
||||
if n.kind == E_REC and n.kids != null {
|
||||
if n.kind == E_NEW and n.s != null and n.a != null and n.a.kind == E_REC and n.a.kids != null {
|
||||
var i = 0
|
||||
while i < len(n.kids) {
|
||||
let f = n.kids[i]
|
||||
if f.s != null and fr_is_frame_field(f.s) and f.a != null and f.a.kind == E_FNREF { fr_mark_root(f.a.s) }
|
||||
while i < len(n.a.kids) {
|
||||
let f = n.a.kids[i]
|
||||
if f.s != null and fr_is_frame_prop_field(n.s, f.s) and f.a != null and f.a.kind == E_FNREF { fr_mark_root(f.a.s) }
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +90,7 @@ function fr_is_textish(n: Node) -> bool {
|
|||
}
|
||||
|
||||
function fr_alloc_kind(n: Node, text_above: bool) -> pointer {
|
||||
if n.kind == E_NEW { return `new:{n.s}` }
|
||||
if n.kind == E_NEW and n.b == null { return `new:{n.s}` } # a dispatch's `new` fills the queue's kept record (n.b)
|
||||
if n.kind == E_LIST { return "list" }
|
||||
if n.kind == E_BIN and (n.s == "+") and not text_above and fr_is_textish(n) { return "text" }
|
||||
if n.kind == E_CALL and n.a != null and n.a.kind == E_ID and n.a.s != null {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue