Methods
(static) benchAaAppend(depth, iterations) → {float}
CANDIDATE (b) — the ledger held in GetGlobalAA(), BrightScript thread-local memory. No node field is touched at all, so this pays neither a rendezvous nor the array-marshalling cost of writing an roSGNode array field. This is the per-launch cost of an APPEND-ONLY ledger; the walk moves to the audit.
| Name | Type | Description |
|---|---|---|
depth | integer | |
iterations | integer |
- Type:
- float
(static) benchAaAppendVerified(depth, iterations, floor) → {object}
The same append, but RETURNING THE RESULTING LENGTH so the caller can prove the pushes actually happened. The first run of this cell read -0.84 us — i.e. below the apparatus floor — and "too cheap to measure" and "optimised away entirely" are indistinguishable from a negative number alone. finalCount decides it: it must equal depth + iterations exactly.
| Name | Type | Description |
|---|---|---|
depth | integer | |
iterations | integer | |
floor | float |
- Type:
- object
(static) benchAaRecord(depth, iterations, skipLCase) → {float}
THE COMBINED CANDIDATE — the ledger held in GetGlobalAA (free storage, no node field read or write) but still pruned on EVERY launch.
Pruning every launch is not gold-plating: pruneTaskLedger is what de-duplicates a relaunched node against the ledger, and a persistent Task node gets relaunched many times (ExtrasRowList alone relaunches its nodes 38 times). Skip it and the same live thread is counted 38 times, which would trip the watermark on an app running 11 threads. So this measures the honest floor for a CORRECT ledger that pays no node-field cost.
| Name | Type | Description |
|---|---|---|
depth | integer | |
iterations | integer | |
skipLCase | boolean |
- Type:
- float
(static) benchCount(depth, iterations) → {float}
| Name | Type | Description |
|---|---|---|
depth | integer | |
iterations | integer |
- Type:
- float
(static) benchCountNoLCase(depth, iterations) → {float}
CANDIDATE (c) — the liveness walk without LCase(), which allocates a string per entry per call. Roku documents the state values as lowercase; this measures what dropping the defensive fold would actually buy before anyone trades correctness for it.
| Name | Type | Description |
|---|---|---|
depth | integer | |
iterations | integer |
- Type:
- float
(static) benchCounterOnly(store, iterations) → {float}
CANDIDATE (a) — an O(1) monotonic counter on m.global. Needs no decrement because it counts LAUNCHES SINCE THE LAST AUDIT, not live threads, which is what killed the original counter design (it would have needed an observeField("state") per launch to decrement).
| Name | Type | Description |
|---|---|---|
store | object | |
iterations | integer |
- Type:
- float
(static) benchFloor(iterations) → {float}
The apparatus floor: the same loop doing the cheapest possible body. Whatever this reads is measurement overhead present in every cell above, and it is SUBTRACTED rather than argued to be negligible.
| Name | Type | Description |
|---|---|---|
iterations | integer |
- Type:
- float
(static) benchNodeArrayPush(store, depth, iterations, floor) → {object}
CANDIDATE (e) — mutate the node's array field IN PLACE instead of rebuilding and writing it back. recordTaskLaunch today does read + rebuild + WRITE, and the write is what marshals. If reading an array field yields a reference rather than a copy, push alone is enough and the ledger can stay on m.global — which sidesteps GetGlobalAA's thread-scope question entirely.
verifiedGrowth is the whole point: if the field read returns a COPY, the pushes land on a throwaway and the field never grows. Cheap AND wrong would otherwise look exactly like cheap AND right.
| Name | Type | Description |
|---|---|---|
store | object | |
depth | integer | |
iterations | integer | |
floor | float |
- Type:
- object
(static) benchPrune(depth, iterations) → {float}
| Name | Type | Description |
|---|---|---|
depth | integer | |
iterations | integer |
- Type:
- float
(static) benchReads(iterations, floor) → {object}
THE DISCRIMINATOR, and the single number this whole component exists to take.
Off the render thread the spec measures 1.56 us for a node the reading thread owns against 91-118 us for a render-owned Task node and 93 us for m.global (Stick 4K). If those three collapse onto each other HERE, every node the ledger touches is render-owned and the production hot path pays no rendezvous at all. If globalUs stays high, m.global is NOT render-owned and a production ledger pays a crossing per launch — which is the design fork this measurement decides.
| Name | Type | Description |
|---|---|---|
iterations | integer | |
floor | float |
- Type:
- object
(static) benchRecord(store, depth, iterations) → {float}
| Name | Type | Description |
|---|---|---|
store | object | |
depth | integer | |
iterations | integer |
- Type:
- float
(static) freshTask() → {object}
ServerReachableTask's init() only sets functionName — no thread, no server. A never-launched Task node reports state = "init", which taskThreadIsLive counts as live, so depth is built without starting a single thread.
- Type:
- object
(static) gaaProbe() → {boolean}
Writes this (render) thread's GetGlobalAA sentinel and starts the probe Task.
- Type:
- boolean
(static) gaaProbeResult() → {object}
Reads BOTH directions once the probe Task has finished.
- Type:
- object
(static) init() → {void}
- Type:
- void
(static) ledgerCost() → {object}
What the ceiling has cost so far, in real launches on real screens.
- Type:
- object
(static) liveCount() → {object}
The live Task-thread count right now, derived from the ledger launchTask() maintains in EVERY build — there is no conditional hook to be missing.
Returns tracked alongside live deliberately: a tracked of 0 means the ledger recorded nothing, while a live of 0 with a non-zero tracked means the app genuinely runs no Task threads right now. Those are opposite findings and a bare live conflates them — the peak gate asserts on tracked for exactly that reason.
- Type:
- object
(static) pruneLedgerNoLCase(ledger, node) → {object}
pruneTaskLedger without the LCase() fold, to price that defence separately.
| Name | Type | Description |
|---|---|---|
ledger | object | |
node | object |
- Type:
- object
(static) resetLedgerCost() → {boolean}
- Type:
- boolean
(static) runCell(args) → {object}
Runs one cell of the bench grid and returns its mean microseconds.
measured against so the caller never has to pair them up itself.
| Name | Type | Description |
|---|---|---|
args | object | { cell: string, depth: integer, iterations: integer } |
an AA carrying the reading(s) for that cell, plus the floor it was
- Type:
- object
(static) seedLedger(store, depth) → {void}
| Name | Type | Description |
|---|---|---|
store | object | |
depth | integer |
- Type:
- void
(static) selfLaunchAndCount() → {object}
THE DISCRIMINATOR the earlier probe could not be.
gaa-thread-scope varied the THREAD and the COMPONENT at the same time, so an empty result was equally explained by "GetGlobalAA is per thread" and by "GetGlobalAA is per component" — and the ledger reading 0 across two render-thread components says the second is what is happening. This holds the thread fixed (render) and the component fixed (this one): launch through launchTask here, read the ledger here. A non-zero count means the ledger works WITHIN a component and the earlier conclusion was wrong.
- Type:
- object