Repository navigation
R5 performance slice A: seven byte-identical speed lanes integrated (INT17) - #279
Merged
Merged
Conversation
…irst read (R5 lane PERF-P1) A host latches the continuation at a script point (the engine's last_script_continuation_* snapshot) and broker_state_hash() folds that latch for as long as it stands. The latch was one full continuation_hash(): for a Pine run, the whole driver log (four points a bar) and the whole command history folded into their digests at the end of the run, about a fifth of every run (PERF0-P), although nothing on the benchmark or report path reads it. NativeExecutionConsumer::capture_continuation_view takes it as a VIEW instead. continuation_hash()'s fold is now a generic lambda instantiated for two sinks: Fnv, unchanged, folds as before; FnvRecord appends the bytes Fnv would fold (in the order bytes() reads them) to a consumer-owned buffer, and leaves the command history's and the driver log's digests behind as holes at the lengths the two logs have (if constexpr); the account digest is folded as each row is appended, so it is recorded like any other value. The 42 hash helpers take either sink (template <class F>), so every eager fold -- a per-bar reader's, a KernelRecorded report point's, the digests' tails -- is compiled as it was: spark gcc 13 gives the Fnv instantiations of hash_handle, hash_bar, Fnv::scalar, Fnv::s and fold_driver_digest the base's exact sizes. (A first cut that branched on a record pointer inside Fnv's primitives cost per-bar readers 5-10 %.) The fold stays inside continuation_hash()'s body, where check_native_cpp_versions.py reads its facts. The first read (latched_continuation, both broker_state_hash_projection overrides) carries the two digests to exactly those lengths and folds the bytes around them, eight at a time through scalar() (fold_recorded): the value continuation_hash() answered at the capture, by construction, since the recording IS that fold. Two invariants make a view foldable, both enforced here: - materialise before advance: continuation_hash() folds a pending view before it carries the digests to the logs' ends (sync_history_digest and the new sync_driver_digest take the target length); - drop at begin: begin_ready drops a view before reset_run_state and before it clears the logs -- the only place they are cleared -- so a view never outlives its run. record_script_report_point (KernelRecorded) drops one before it rewrites the latch eagerly. A buffer that cannot grow ends the recording and the capture folds at once, so the value never depends on memory. Debug builds prove every view against the eager fold at capture and hold the invariants at fold time (abort). defer_continuation_views(false) folds at capture: the eager spelling a view reproduces, kept as the witness's reference. No public header moves: the state lives in the consumer, which the engine holds by unique_ptr; hosts are neither copyable nor movable (native_host.hpp), so a latch never leaves its consumer. No value moves: no epoch. Witness (kernel-only row): tests/test_native_continuation_view.cpp -- one bare host, three spellings of one latch (Base: the host latches native_continuation_hash() itself; Eager; Deferred), six scenarios (batch with reads long after a latch and a live read in between, KernelRecorded, abort, stream warmup + realtime, a reused host whose unread view the next begin drops, a begin refused at preflight after which the view still reads), every read equal across the three and to values pinned on fc7aad6 (PINEFORGE_P1_HARVEST; zone "UTC+0", no tzdata in the digest), plus direct probes: a live read folds a pending view first, no view survives a begin, a Deferred run returns with its view pending. 176 checks. Fail-before (fc7aad6): "no member named 'defer_continuation_views' in 'pineforge::NativeExecutionConsumer'". Mutations each fail it: no materialise-before-advance (line 132), no drop at begin (line 118), no drop at the KernelRecorded point (kernel_recorded read[0]), a wrong recorded bool byte (batch read[1]), the account digest left unrecorded (batch read[1]), a replay that drops a segment's tail (batch read[1]). ADR-0001 rule 2: generic. (a) Mechanism: any host that latches its continuation can latch a view; nothing in it knows Pine. (b) Knob: no hash-visible choice -- every value is the eager capture's, and the switch is an internal consumer member on no public surface. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit c099bae73498cff2141acf3ab5dd4ebee8c6d94b)
… lane PERF-P1) PineStrategyHost::capture_script_continuation_hash latches the continuation at the run's last script point -- the last batch bar, each leftover input after an aggregated chart's last bucket, every realtime bar of a stream -- so that broker_state_hash() is one value with recording on or off (R5 F3). Unless a recorded row needs the value at once, it now latches a view (NativeExecutionConsumer::capture_continuation_view) and broker_state_hash_projection reads it through latched_continuation, which folds it on the first read. With recording on, the capture and scheduler_record_broker_hash stay eager and drop any view first, because the row they write folds the value now. Nothing on the benchmark, the C report or the corpus sweep reads the scalar, so a Pine run no longer pays the terminal fold: 0.79x run time in PERF0-P's prototype of the same mechanism, measured for this tree in the lane report. This revisits E24's STOP bucket and F9's decision row "the terminal capture stays eager" (design §3.8 C). Those were campaign design notes, not owner rulings; the owner's red line is hash values, which the view keeps by construction. Supervisor ruling, recorded in the design and ADR-0001 in the next commit. Witness (release row): tests/test_adapter_continuation_view.cpp -- eleven scenarios over every capture site (chart timeframe, bar magnifier, aggregated with leftover input with and without the magnifier, calc_on_order_fills, recording on, a stream, a stream that aborts, a batch abort, a reused host whose unread view the next begin drops, a begin refused at preflight), each run on two hosts that differ only in defer_continuation_views: every read -- the live fold and the latch from inside the script, after Completed, after the next begin, after the refusal, the recorded rows -- equal between the eager and the deferred leg and to values pinned on fc7aad6 (PINEFORGE_P1_HARVEST; syminfo zone "UTC+0"). A deferred batch run returns with its view pending and a read folds it. 87 checks. Fail-before (fc7aad6): "no member named 'defer_continuation_views' in 'pineforge::NativeExecutionConsumer'". Mutations fail it: a projection that ignores the view (chart read[4] against the pin), a recording capture that defers (recording read[7]). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 914387fc51cb42a9e2af36d9ddc488135dbe51c9)
…vised, ADR-0001 ruling row (R5 lane PERF-P1) Design §3.8 (audit lane F9), row C: the terminal continuation capture was "decided: kept eager" (E24 STOP1). Supervisor ruling, recorded here: E24's STOP bucket and F9's decision row were campaign design notes, not owner rulings; the owner's red line is hash values, and a view keeps every one by construction, so the capture is revised to a view folded on first read. The F9 decision stays on the page as history; the revision states the mechanism (the fold's own bytes recorded into a second sink, two digest holes), the two rules that keep it exact (materialise before advance, drop at begin), what stays eager (a recorded row, a KernelRecorded report point), the witnesses and the measurement: the gated replay 0.2618 -> 0.2029 s on macOS (7.71-9.08x -> 6.58-7.19x of ab9714b, load 36-41) and 0.4054 -> 0.3606 s on spark (12.87x -> 11.45x); the Pine benchmark 0.84x on spark (14 shared probes and 100 public slots, magnifier off and on). The section's opening sentence now says the recording-off path latches the scalar as that view. ADR-0001, "Kernel state the adapter sets": a ruling row for the latched continuation (the engine's last_script_continuation_* and the consumer's view beside it) -- who writes it and when, and rule 2: (a) a generic mechanism any latching host may use, (b) no knob, since no value can move. Every other hunk is digits only: the lane's source edits moved lines the pages cite, re-anchored mechanically with check_doc_anchors.py --fix and reanchor_doc_citations.py --ref fc7aad6 (ADR-0001, the design record, docs/pages/native-engine.md and docs/pages/pine-to-native.md). check_doc_anchors: 1631 of 1631 anchors resolve; check_doc_lint: 0 offenders. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 3bd72fbc0b6510bf366a2fe74a266a55edaf302f)
…y hook (R5 lane PERF-P23) The ProjectionMismatch contract as a pinned matrix: every field NativeExecutionConsumer::projection_ok compares (the capital, point value, FX scalar and curve arrays, tick, commission kind and value, the ten instrument and zone strings) is written from every NativeStrategyHost virtual and BacktestEngine::hash_host_extension, at the hook's first and third call, over batch, aggregated, margin, anchored, lower-timeframe, subscription, tick-stream and confirmed-bar-stream runs. Each write's whole outcome is pinned: whether the hook ran, the lifecycle, the NativeFailure code / operation / ordinal / discriminator / context kind and the error text -- 900 writes, 720 of them caught as ProjectionMismatch. The unwritten run of every profile completes and reaches every hook its cells name. Harvested at the lane's base fc7aad6 (PF_PROJECTION_WITNESS_DUMP=1); the lane's projection trims must leave it unchanged. Source-free, so it runs in the kernel-only profile too (+1 kernel row, +1 release row). No kernel change (ADR-0001 rule 2 not engaged). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 35d7e972a3222396cb6457862eeb3e6fcd9812f4)
… can have moved, and compares its strings inline (R5 lane PERF-P23, a)
The ProjectionMismatch guard ran 4.1-4.5 times per bar on the Pine path,
comparing ten strings each time through std::string's out-of-line compare
(PERF0-P: 134 ns/bar, std::char_traits<char>::compare 7 % inclusive).
1. pump_batch: an input after the first no longer re-compares the
projection before it is consumed. The previous input's closing check,
check_abort_or_projection(Input), is the loop's last statement; when it
passes, control reaches the next pre-input check through `++i; i < n`
alone -- no call, so no host code and no consumer state change in
between. projection_ok is a pure function of the engine's projected
fields, the state_ spec and staged_fx_curve_, none of which moved, so the
compare it would repeat answers true by construction. What another thread
may legally change in that window is the atomic abort flag, and
check_abort (check_abort_or_projection's first half, split out verbatim)
still reads it every input with the same failure {Aborted, Input, 0} and
the same text. The first input keeps the full check: begin_ready's last
check precedes it.
2. projection_ok compares the ten strings in their established order by
std::string's own equality -- equal length, equal bytes -- written out
eight bytes at a time in one loop (a helper was left out of line by
clang's cold-callsite threshold). The answer is a pure boolean, so the
same equality in the same order returns the same value for every input.
Contract: tests/test_native_projection_witness.cpp (900 writes from every
host hook, pinned at fc7aad6) is unchanged.
Ruling (ADR-0001 rule 2 not engaged): no capability, spec field, hook, hash
input or readback changes; both edits are platform-free implementation
details of the existing guard.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
(cherry picked from commit 47517048bdb782642294fe6825f39027d09f2016)
…f taking its own (R5 lane PERF-P23, b)
NativeExecutionConsumer asked dynamic_cast<NativeStrategyHost*> for the
engine it was handed at 18 sites -- three to five times a bar on the Pine
path (margin_check_admitted, the input / bar-open / calculation callbacks,
and the fill-path hooks), each a libc++abi walk of the host's class chain
(PERF0-P: __dynamic_cast 4.9 % inclusive, 72 ns/bar with the adapter's).
prepare_public_begin, the first thing every public begin does, now takes the
one downcast and keeps it beside the engine it was taken for
(downcast_engine_ / downcast_host_, consumer members: the consumer is
internal, held by BacktestEngine's slot, never copied or moved between
engines -- a copied or moved engine gets a fresh consumer). native_host()
answers that pointer for that engine and dynamic_cast directly for any
other, so its answer is dynamic_cast's own: the owning engine's dynamic type
cannot change while its consumer lives (its consumer is destroyed in
~BacktestEngine's member teardown, after every derived destructor, and no
site runs from a destructor), and a site reached before any begin, or with
another engine, falls through to dynamic_cast. The 17 other sites read
native_host(engine); locals keep their names.
Witness: tests/test_native_callback_caches.cpp compares native_host() with
dynamic_cast inside every host hook and around every public call over 954
randomized runs (two host classes, batch / stream / abort / exception /
reuse lifecycles, a non-native engine and a second live host asked about)
-- 205,500 comparisons. At the base it does not compile ("no member named
'native_host' in 'pineforge::NativeExecutionConsumer'"). Source-free (+1
kernel row, +1 release row).
Ruling (ADR-0001 rule 2 not engaged): no capability, spec field, hook, hash
input or readback changes; the cache is derived consumer state, never
folded.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
(cherry picked from commit 0fd137b1befdfd6c4bb42e3dba4b0a9884e2aacd)
… at every call site (R5 lane PERF-P23, b) PineExecutionAdapter asked dynamic_cast<PineStrategyHost*> of its bound host at 25 sites; the account-FX lookup (active_staged_fx), the percent-commission sizing basis (percent_commission_live_equity) and the leg-order question (source_path_uses_high_first) alone run 1.7-5.5 times a bar on the public probes (scratch counters: barstate 1.7, scalping 3.8, pyramid 5.5). pine_view(host), TU-local in pine_adapter.cpp, answers dynamic_cast's own result from a per-thread memo keyed by the three facts that fix it: the host's address, the address of its complete object (dynamic_cast<const void*>) and that object's dynamic type (typeid). A host built at an address a different type used before therefore misses and is re-derived; construction and destruction phases read the same vtable dynamic_cast would. The adapter gains no member: it is a by-value member of PineStrategyHost, whose layout is the generated script's ABI, so a member would move it. Every site now reads pine_view(host_) / pine_view(&require_host()) / pine_view(&host). Witness: tests/test_adapter_host_view_memo.cpp runs two Pine host classes that reach those sites every bar (percent-of-equity sizing under a percent commission with pyramiding, a staged FX series in batch) solo as batch and stream, as two live streams interleaved bar by bar, three at once in a round robin, and rebuilt in turn in one block of storage; all 13 runs' trades, net profit and broker-state hash are pinned from the lane's base fc7aad6, and every shared or rebuilt run equals its class's solo run. It passes at the base by construction (it pins behaviour through public API only); the memo's own keying is what the interleaved and rebuilt runs stress. Source-bound (+1 release row). Adapter-only; no kernel change (ADR-0001 rule 2 not engaged). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit da08473f353fcd69c3dfd30d7922cbec88494da9)
… cache (R5 lane PERF-P23, c)
spec_ptr() probed the lifecycle variant and the three label predicates
(has_undetected_timeframe, legacy_tolerant_slot_labels,
uses_raw_label_partition) re-derived their answers from it at every per-bar
interval lookup, decision context, callback and check (PERF0-K K7: spec_ptr
3.4 % of the bare idle kernel, the predicates behind every
input_interval_at / script_interval_at).
begin_ready now takes them once, right after state_ becomes NativeRunning
(cache_running_policy: the three predicates are evaluated by the probing
path, then the spec's address is kept). They are valid for exactly as long
as state_ holds NativeRunning:
- that alternative's spec lives in place inside state_, so its address is
fixed while the alternative is;
- no in-run path writes the fields the predicates read -- the two begin-time
declarations rewrite `subscriptions` and `auxiliary_feed` alone -- and
pairing_ is written only by configure and apply_spec, both before Running
(configure refuses a Running host, through latch_failure);
- every other write of state_ is preceded by leave_running(): latch_failure,
configure's Ready, the three batch completions and stream_end.
With the pointer null, spec_ptr() and the predicates run their old bodies
unchanged, so every answer is the probe's.
Witness: tests/test_native_callback_caches.cpp now also compares spec_ptr(),
the three predicates and native_state().spec with the pre-lane probe and
formulas (restated verbatim over the consumer's own variant and pairing,
through a friend probe) inside every host hook and around every public call
-- 376,750 comparisons over 954 randomized runs. At the base it does not
compile ("'spec_ptr' is a private member of
'pineforge::NativeExecutionConsumer'"). A scratch mutation that drops
leave_running() from the completions and configure fails it 537 times.
Ruling (ADR-0001 rule 2 not engaged): no capability, spec field, hook, hash
input or readback changes; the cache is derived consumer state, never
folded.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
(cherry picked from commit 739e420f5d37cded472d662b1a5c510da88e57aa)
…ng its length (R5 lane PERF-P23) The witness's string writes all appended a byte, so every one was caught by the length comparison and none reached the byte comparison the lane's projection trim rewrites. Each of the ten projected strings is now also written at its first and at its last byte with its length kept, and the spec's strings run 3, 4, 6, 8, 9, 16 and 41 bytes long, so a same-length write is caught through every path of the comparison (the three-byte, the two-half-word, the one-word, the word-plus-overlapping-tail and the word loop). 1,900 writes, 1,520 caught as ProjectionMismatch; where a write is caught still depends on the field in one row only (margin_check_allowed's third call, initial_capital). Re-harvested at the lane's base fc7aad6 and unchanged on the lane's tree. No kernel change (ADR-0001 rule 2 not engaged). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 6038d49d7267cdc5944c2b1e3d65255ad2d53553)
…pping, never byte by byte (R5 lane PERF-P23, a)
Measured, the first inline spelling of the ten-string compare (eight-byte
words, then a byte loop for the tail) cost MORE than libstdc++'s operator==
under gcc 13: on spark it added 53-103 user instructions a bar on two
public probes (P004 1,701.7 M -> 1,704.5 M, P021 2,384.5 M -> 2,390.0 M),
because glibc's memcmp beats a byte loop over the three-to-seven-byte
tails these strings have. Under Apple clang it did pay, because libc++
routes operator== through an out-of-line char_traits::compare.
same_text now reads every byte inside one of a few word loads: n >= 8 is a
word loop whose last word overlaps the word before it; 4 <= n < 8 is two
half-words (the second overlapping); n <= 3 is the first, middle and last
byte, which are all of them. Every index in [0, n) lies in at least one
compared window of both strings and no load leaves [0, n), so the answer is
equal length and equal bytes -- std::string's own equality. The nine SymInfo
strings are walked through a constant member-pointer table in their
established order, chart_timezone last, as before; projection_ok stays a
pure boolean.
Measured against the first spelling, on spark (gcc 13, core 15): P004
1,551.1 M -> 1,509.5 M instructions, 358.8 M -> 351.0 M cycles; P021
2,183.7 M -> 2,138.3 M, 562.5 M -> 550.5 M. On this Mac (Apple clang 17,
14 shared probes, load 13-18): lane tip / base 0.896 -> 0.880 magnifier off,
0.904 -> 0.882 on.
Witness: tests/test_native_projection_compare.cpp holds
NativeExecutionConsumer::same_bytes (the function projection_ok compares
with, exposed to the consumer's test probe) against operator== exhaustively
-- every length to 40, every single-position difference by every bit -- and
over 2,000,000 randomized pairs of lengths 0..72 with every byte value, in
both argument orders: 4,013,446 comparisons, 0 mismatches. At the base it
does not compile ("no member named 'same_bytes' in
'pineforge::NativeExecutionConsumer'"); a scratch mutation dropping the
middle-byte compare fails it 4,592 times. tests/test_native_projection_witness
(1,900 writes, same-length string writes included) is unchanged. Source-free
(+1 kernel row, +1 release row).
Ruling (ADR-0001 rule 2 not engaged): an implementation detail of the
existing guard; no capability, spec field, hook, hash input or readback
changes.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
(cherry picked from commit 6c810e7b308e29a3cd04759a6dc1e3eb0cae6b0e)
…fter takes its slice from the same search (R5 lane PERF-P4, step 1) A host that watches its own receipts reads the command events above a cursor. Until now the only read was events_after (native_events): an owning copy of every row above the cursor -- the command events, but also every driver point and account observation, each in a 712-byte NativeMarketEvent -- into a fresh vector. PERF0-P measured the Pine adapter's three such reads a bar at 95 ns/bar and most of its per-bar allocations; on a quiet bar they copy four or five driver points to learn that no command arrived. 1. first_command_after(after): the first history index whose ordinal is above `after` (the history's size when there is none), found exactly as events_after found its command slice -- walk back from the tail, bisect a suffix longer than 32. events_after now takes its slice from it, so the in-place rows and the materialised command rows are one computation. 2. visit_commands_after(after, visit): visits those events as the history stood when the call began, in history order, with one copy per event -- the copy each materialised row was -- and stops at the first true. A visitor may append to the history (a cancel it issues) without seeing what it appended or holding an event the append reallocated. The premise, a command history in strictly increasing ordinal order, is the request core's own: usable_ordinal refuses a reused or regressed ordinal, a multi-event plan numbers its events ordinal, ordinal + 1, and event_at already bisects the ordinal index. events_after answers what it answered. Witness: tests/test_native_command_history_read.cpp, source-free (kernel-only and release profiles). Over a randomized command stream (market, limit, stop, stop-limit, cancel and reduce groups, replaces, cancels, flattens, armed brackets, host-refused candidates), with and without a synthesized intrabar path: for every cursor from 0 to one past the high water after the run, and sampled cursors inside every callback kind during it, the in-place events are native_events' command rows in order; a reader that advances a cursor over the rows holds the same cursor at every command visit and ends on the same value; a visitor that appends enough to reallocate the history sees exactly the snapshot; a commandless span allocates nothing. Fail-before, compiled against fc7aad6: tests/test_native_command_history_read.cpp:137:18: error: no member named 'visit_commands_after' in 'pineforge::NativeExecutionConsumer' Ruling (ADR-0001 rule 2 not engaged): a generic, internal read of the kernel's own command history. No capability, spec field, hook, hash input, readback or public header changes; the two members live in the internal src/native_execution_consumer.hpp and name nothing platform-specific. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 2e8773f57a4bd8a014a33a7facffe11aa5c1cef6)
…instead of materialising native_events (R5 lane PERF-P4, step 2) observe_terminal_receipts (three times a bar: the bar open, the bar's calculation, the source publication), the exit precommit's same-bar declined-reversal test (follows_same_bar_declined_reversal) and the margin call's bracket revival (revive_brackets_after_margin) each copied native_events(receipt_cursor_) -- every row above the cursor -- into a fresh vector. They now visit the command events in place through NativeExecutionConsumer::visit_commands_after, one copy per event. The consumer is reached through PineStrategyHost::adapter_receipt_consumer, an inline static accessor beside the two receipt readers (no layout change). - The receipt cursor moves exactly as it moved over the materialised rows. Those were in ordinal order, commands first at a tied ordinal, so at each command the cursor held that command's ordinal, and after the last row it held the high water of the three logs; here it takes each command's ordinal before that command is observed, then the high water read before the visit. Events a handler appends (cancel_bracket_siblings, cancel_bracket_origin) land above that high water -- past the end of the old snapshot -- so the next read observes them, as before. - The no-new-event early return no longer excludes the bar magnifier: with nothing above the cursor the materialised loop was empty. The terminal watermark is a separate matter: terminal_receipt_cursor_ is folded into the broker-state hash (pine_state_hash.cpp) and has only ever advanced on a run without an intrabar path, so it keeps exactly that (it stays 0 under the magnifier). - The in-place read is taken only with the receipt readers installed, which only PineStrategyHost does, at every begin, on its own adapter; any other binding keeps the materialised read. Witness: tests/test_adapter_receipts_in_place.cpp. Seven receipt-heavy scenarios, magnifier off and on -- a declined reversal that kills its bracket while the stop is touched on the same bar (the precommit test) and re-issues it; a margin call whose revival reads a same-bar decline; one whose revived stop cascades; brackets whose filled leg cancels its sibling from inside the read, and cancelled entries with pending brackets; cancel and reduce groups (a ReservationReducedEvent); stop-limit activations; a quiet tape. Both cursors at every source bar and at every hash the host folds, every recorded broker-state hash row, the final scalar and every trade, pinned on fc7aad6 with -DPINEFORGE_P4_HARVEST. And a commandless read (driver points and account rows only) allocates nothing. Fail-before, the same TU against fc7aad6 (the pins pass there by construction): commandless receipt read (magnifier off): 1 allocations FAIL tests/test_adapter_receipts_in_place.cpp:1261 allocations == 0 commandless receipt read (magnifier on): 1 allocations FAIL tests/test_adapter_receipts_in_place.cpp:1261 allocations == 0 Ruling (ADR-0001 rule 2 not engaged): adapter-internal; the kernel gains nothing here, and no hook, hash input, readback or header layout changes. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 0d61770ad5897b181275ac2c5eb0f93bb5b1c930)
…tcher changes (R5 lane PERF-K3, step 1) Lane PERF-K3 changes how match_path chooses its next winner, never which winner it chooses. Before the change lands, this row pins what five books produce on the tree the lane starts from, value by value, so every later step is held to the same values: - the continuation hash read at every bar and at every applied fill, folded into one digest (the continuation folds the whole command history, the driver log and the account log); - the final continuation and broker-state hashes; - every trade (times, prices, quantity, P&L, commission, incarnation, bar indices), the event census and the host's own counters. The books come from tests/native_match_book_fixture.hpp: one seeded host that keeps 5 to 100 requests working and churns them every bar -- every intent (Transact, Reduce, Flatten, a host-sized cohort close), every trigger kind, OCA groups under both effects, per-point budgets, brackets whose legs wait for their entry under either arm scope and first-match rule, cohort rosters, replaces, cancels and requests born in the fill callback -- over a tape that gaps through resting levels, under every intrabar path, calculate-on-fills and the quantizing price grid. Every price is a whole number of quarter ticks and the run is UTC, so the values are portable: harvested with -DPINEFORGE_K3_HARVEST against the fc7aad6 archives on macOS arm64 (AppleClang 17) and Linux aarch64 (GCC 13.3), the two harvests are identical. No kernel change (ADR-0001 rule 2 is not engaged). The row is source-free, so it registers in every profile the tests build: KERNEL_MIN_TESTS 213 -> 214 and RELEASE_MIN_TESTS 586 -> 587 when the integrator counts it. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 4adb17760f99bb12df8812f1246c72df226d5782)
… heap of its rows (R5 lane PERF-K3, step 2) RULING (ADR-0001 rule 2): generic. A cost change inside the kernel's own matcher that every host shares; no TradingView fact enters it, no spec field, hook or knob is added, and no value a host can observe moves. match_path picks one winner at a time. Every live request that refresh_point_allowances does not refresh in bulk -- every Transact, every owner-bound close -- is an Evaluate winner of its own at each driver point, and every winner was followed by a scan of the whole working book, so K such requests cost K scans of K rows per point. Lane PERF0-K measured 10 resting limits at 9.5 us/bar and 100 at 626 us/bar on spark (June: 0.78 and 14), the matcher being 48-94 % of the bracket, re-issue and resting workloads. Exactness, re-derived on fc7aad6. Let W be an Evaluate winner whose refresh_allowance returns true while W's t equals the cursor bit for bit. The next full scan would differ from the last one in W's row only: - refresh_allowance (native_order.cpp:2111-2148) writes W's allowance and bumps the core's epoch, nothing else: no live row is added, removed or moved, no event is appended, no trigger state, remaining, pending, roster or engine field changes, and nothing is installed, so clear_cohort_target_cache is not called either. - The cursor stays: t_cursor keeps its bits, cursor_price is unchanged (an Evaluate row's price IS cursor_price when its t equals the cursor), path_cursor is rebuilt identical, observe_trails and apply_excursion do not run, and the skip set is cleared only when t_cursor changes and gains nothing on a refresh. - Every input of another request X's row is X's own: eligibility_facts (native_order.cpp:1677-1738) reads X's LiveRequest and the evaluation context only; cause_floor, needs_evaluation, armed_after_print_here, born_on_remaining_path, level_for, working_is_buy and request_is_buy read X, the unchanged point and cursor and the unchanged engine; pre_open_birth_eligible (native_execution_consumer.cpp:3904-3913) reads consumer state only a callback writes; the geometric hit functions are pure. None reads the epoch or W's allowance. - The cohort target cache (native_execution_consumer.cpp:2555-2571) was consulted for X by the last full scan and nothing cleared it since, so X's lookup inserts nothing and answers the same observation -- or the same uncached read when all sixteen slots are taken. - Provenance: X's rescan would erase only the rows it erased in the last scan and append a copy of the row it appended then, so every later retained_origin lookup (X's own rows, newest first) answers the same values; and the erase at the loop head would drop nothing, because each row it tests passed at the previous head or was pushed since from the same state. A winner-only pass therefore skips that erase. So only W is rescanned, and every other row is the one already built. Selection: the rows' incarnations are distinct (the working book holds a request once) and every row's t is finite in [cursor, 1] (cause_floor takes std::max against a finite cursor and the geometric hits check finiteness), so the scan's order (t, incarnation, kind) is a strict total order with a unique least row, and a binary heap on that same comparison (MatchRows::precedes, now also the scan's own) yields exactly the row the full rescan's sequential argmin picks. Any other outcome -- a fill, a trigger, a mutation, a skip, a failed refresh, a moved cursor -- scans the whole book again, exactly as before. What changed (src/native_execution_consumer.{hpp,cpp}, match_path): - MatchRows, consumer-owned: the kept rows in a buffer that keeps its capacity across calls, so the path is allocation-free once warm, made a heap on the first winner-only pass. Rows are kept from four live requests on (prototype C2's threshold); below that nothing is kept. - Each kept row carries its working-book index, and the winner's request is read at that index (checked by handle) instead of through the core's handle search: an O(1) lookup on the consumer side, no core layout touched. - set_match_row_reuse(false) restores the full rescan and the handle search. It is not a spec field and no host reaches it; it lets the differential row run both computations. Nothing is hashed that was not. The rows are scratch, cleared on entry to every call; the switch chooses between two computations of the same values. No public header changes (include/ untouched). Witnesses, recorded against fc7aad6 first: - tests/test_native_match_rescan_scaling.cpp: 10 vs 40 resting live requests over the same 3000 bars, CPU ratio < 8. fc7aad6 (macOS arm64): 0.0220 s / 0.2606 s = 11.87 -> FAIL. This commit: 0.0074 s / 0.0277 s = 3.74 -> pass. - tests/test_native_match_row_reuse.cpp: 168 randomized books (1..100 live, every intent and trigger kind, OCA groups, brackets, cohort closes, replaces and cancels every bar, fills at t=0 among the refreshes, every intrabar path, calculate-on-fills, the quantizing grid), each run with the reuse on and off; every continuation observation, both hashes, every trade, the event census and the host counters equal -> 3027 checks pass. Against fc7aad6 it does not compile: "no member named 'set_match_row_reuse' in 'pineforge::NativeExecutionConsumer'". An instrumented copy counted 421,812 winner-only passes over those books, 24,646 of them taking a fill and 23,272 a trigger at the refreshed cursor. - tests/test_native_match_hash_witness.cpp (step 1) keeps passing. Both new rows are source-free: KERNEL_MIN_TESTS and RELEASE_MIN_TESTS each +2 when the integrator counts them. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 74d6179709a063b6fc8aab359a4bcd2c7c0107d9)
…e PERF-K3, step 3) RULING (ADR-0001 rule 2): generic. An internal lookup of the kernel's own request core that every host shares; no TradingView fact, no spec field, hook or knob, and no value a host can observe moves. The public native_order_v6 layout is untouched: no member is added, the order the lookup relies on is one the core already keeps. WorkingRequestCore::classify and find_live (native_order.cpp:866-885 on fc7aad6) walked the whole working book for every handle every command, preparation and install names. After step 2 the matcher reads its winner at the row's own index, but each allowance refresh still names its request to the core (refresh_allowance -> classify), so a refresh pass over K live requests kept a K^2 walk. The working book is in strictly increasing incarnation order, always: WorkingRequestCore::commit is the only writer of live_; a push and an erase-push append a row born under a fresh incarnation, which usable_incarnation makes larger than every incarnation the run issued before; an erase keeps the rest in order; an update rewrites a row in place under its own handle (a retained partial fill, an arm -- whose materialized definition keeps the handle -- a bind, a trigger, a refresh). So the one row that can carry an incarnation is found by bisecting down to a short run and walking that run up to the first row not older than the one asked for; a book of at most sixteen rows is only walked. The answer is the same row the full walk found, because incarnations are unique in the book. Code shape: the lookup is one internal function (live_position, with names_run for the handle's run check) used by classify and by find_live, which no longer calls classify. Measured on spark (GCC 13.3, -O2, perf stat instructions:u), that keeps the few-order runs within the layout noise of fc7aad6 -- replay +0.16 %, bracket +0.31 %, re-issue +0.58 %, market -0.53 %, idle -0.81 % -- where a bisecting classify that find_live called cost the re-issue run +1.07 %; and it takes 15 % off 100 resting requests against the walk (348.8 M against 410.1 M instructions). Witness: tests/test_native_match_row_reuse.cpp gains the_core_finds_exactly_the_handles_it_holds, run first: a bare core driven through submit / replace / cancel to 200 live rows and back to 6; every replace and cancel must answer what a walk of live() predicts, and after every command each live handle, retired handles, a handle of another run and a zero incarnation are looked up against the walk (265,758 lookups). Mutation check: bounding the walk to the bisected run (dropping the row at its upper edge) makes it report 18,787 failures. The randomized books of step 2 also check at every bar that the working book is in incarnation order. No new row: the counts of step 2 stand. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit ab25b1d056949959587a109745e631c0345273e1)
…rch (R5 lane PERF-K24, K2) deliver_intrabar_script chose a script bar's sub-bars by testing every bar of IntrabarPath::lower_tf against the window [base.open_ms, next_input_open_ms), so each script bar cost the length of the whole feed and a lower-timeframe run was quadratic in its length (lane PERF0-K: 47 us per bar at 20,000 bars on spark). The scan ran even where no window can hold a bar: under a raw-label partition (FeedTolerant with a Passthrough pairing, the source adapter's own policy) every window is zero-width and every script bar falls back to its own OHLC path after scanning the feed. preflight_intrabar_path runs preflight_native_inputs over the lower feed before every begin (run_simple, run_tf, run_rich, stream_begin), and both of its label branches refuse a stamp that is not strictly greater than the one before it (NotStrictlyIncreasing, src/market_driver.cpp:135-141 and :170-176 on this tree). Nothing mutates the retained feed after configure_native. So the bars of one window are one contiguous run of the feed in feed order: std::lower_bound to its first bar and a walk to the first stamp at or past the window's end select exactly the bars, in exactly the order, the scan selected, and nothing else changes. Measured on spark (Linux aarch64, gcc 13, Release, taskset -c 18, best of 5 interleaved, 1-min load 5.3-6.0), bracket host, raw labels, lower path: bars before (scan) after ns/bar before -> after 5,000 60.4 ms 7.2 ms 12,076 -> 1,444 10,000 234.2 ms 14.8 ms 23,420 -> 1,477 20,000 944.9 ms 27.3 ms 47,247 -> 1,367 40,000 4,273.3 ms 61.4 ms 106,833 -> 1,536 Canonical labels, 5 sub-bars per script bar, 20,000 bars (best of 5, load 2.4-2.8): 1.2146 -> 0.2660 s, 60,731 -> 13,299 ns/bar (the rest is the canonical calendar, lane K1). Witness, source-free (kernel and release profiles, +1 row each): tests/test_native_intrabar_lower_lookup.cpp. Shape half: an idle host over a tiling 1-minute feed under raw labels, 4,000 vs 16,000 script bars, best of three process CPU seconds, bound 6. Fail-before against the fc7aad6 library: ratio 15.73 / 15.92 / 15.98 (Mac, M4 Max, clang 17) and 15.88 / 15.71 / 16.06 (spark); after: 3.61 / 3.98 / 4.10 (Mac) and 4.62 (spark, this commit alone). Value half: trades, trade digest, driver-point count, native_continuation_hash() and broker_state_hash() of a bracket host over a tiling feed, a ragged feed (bars before the first window and after the last, windows of none to five bars, a one-bar window through the distribution sampler) and the tiling feed under raw labels, harvested on fc7aad6 with -DPINEFORGE_K24_HARVEST; identical on macOS clang and Linux gcc (fixed-offset zone, no tzdata in the continuation) and unchanged here. Identity battery (PERF0-K bin/identity.sh, 132 configurations, 3,000 bars): identical to fc7aad6 line for line. Ruling (ADR-0001 rule 2): (a) the mechanism is generic -- an ordered lookup over any strictly increasing retained feed, no platform branch; (b) no knob, no surface, no spec field and no hash-visible choice: the selected sub-bars, their order and every value downstream are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 67b38b037c3641234916e73f9909d89e4d6c76c9)
…length (R5 lane PERF-K24, K4) A batch pump appends driver points to driver_log_ at every script bar and command events to the request core's history whenever the host commands. Both vectors grew one doubling at a time, and each regrowth moved the whole log into a freshly faulted block and unmapped the old one (lane PERF0-K: 27-44 % of a bare idle kernel, about 25 % of a bracket, 33 % of the replay as allocator and page-fault leaves). reserve_driver_log existed; the one caller, PineStrategyHost::prepare_native_begin, reserved 4n points and 4n history events, and begin_ready then replaced the request core (requests_.reset), discarding the history half before the run began. pump_batch now sizes both logs for the batch: - The driver log is reserved exactly, up front, where its length is a fact of the spec: a Passthrough pairing (one script bar per input, including a raw-label partition) with no intrabar path (4 points per script bar) or a synthesized path without volume weighting (`samples` points), plus one for NativeCloseExecution::AfterCalculation, plus points/64 + 16 for current executions. The census of PERF0-K's host confirms 4.000 points per bar with no path and with a 4-sample synthesized path, 20.000 with a lower feed of five sub-bars under canonical labels, and 4.000 under raw labels (every window empty). PERF0-K's estimate of (4 + samples) points per bar with a path is not the count on this tree, and a lower feed's count is data (4 per sub-bar, the sampler's count for a one-bar distribution window, 4 for an empty one). - Every other driver log (lower feed, volume-weighted samples, aggregated script timeframe) and every history is sized at doubling checkpoints (64, 128, ... inputs) from the batch's own rate: to its projected end plus an eighth, and never beyond 16 times what the log already holds; a capped reservation is taken only once the log could not absorb another window like the last. A burst early in a run therefore reserves at most 16x the burst, never address space in proportion to the batch (PERF0-K's prototype D reserved 4n x 520 B of history up front, 2 KB per bar). - A reservation the allocator refuses (std::bad_alloc) is dropped and the log grows as before; targets are clamped to max_size(). - reserve_driver_log reserves the driver log alone now: its history half never reached a run (above), and the pump owns the history's sizing through the existing WorkingRequestCore::reserve (src/native_order.cpp unchanged: it already reserves exactly what it is asked for). Capacity is not observable: no hash, readback, event or report reads it. Measured on spark (Linux aarch64, gcc 13, Release, taskset -c 18, best of 5 interleaved, 1-min load 2.4-2.8), base fc7aad6 vs this lane (K2+K4): W1 runtime-budget replay (43,008 bars) 0.4124 -> 0.3508 s -14.9 % W2 idle, tolerant, 1e5 bars 0.0681 -> 0.0423 s -37.8 % W3 idle, canonical, 1e5 0.6297 -> 0.6060 s -3.8 % W4 re-issue, tolerant, 1e5 0.4049 -> 0.2807 s -30.7 % W5 bracket, tolerant, 1e5 0.1809 -> 0.1264 s -30.1 % W6 bracket, canonical, 1e5 0.7468 -> 0.6962 s -6.8 % W7 market, tolerant, 1e5 0.0951 -> 0.0673 s -29.3 % W8 10 resting, tolerant, 2e4 0.1904 -> 0.1865 s -2.1 % W9 bracket, KernelRecorded, 5e4 0.1408 -> 0.1169 s -17.0 % W12 bracket, synthesized path, 1e5 0.1850 -> 0.1321 s -28.6 % scripts/check_runtime_budget.py, paired rounds: spark 12.260 / 12.002 / 12.580x -> 10.730 / 10.727 / 10.667x; Mac (M4 Max, load 8.5-9.1) 8.340 / 8.365 / 8.126x -> 7.926 / 7.771 / 7.771x. Exact history against the bounded one (PERF0-K prototype-D style, 4n + 16; same session, load 3.7-4.9): re-issue 0.2700 vs 0.2831 s, bracket 0.1265 vs 0.1273 s, replay 0.3555 vs 0.3581 s. Peak RSS at 1e6 bars (ru_maxrss): idle 743.8 -> 722.4 MB, re-issue 3566.0 -> 3566.1, bracket 1309.2 -> 1309.3, market 906.9 -> 906.8; the replay 322.8 -> 323.2 MB; a lower feed at 40,000 bars 93.4 -> 87.2 MB. Witness, source-free (kernel and release profiles, +1 row each): tests/test_native_batch_log_presize.cpp counts the global allocator during run(). Fail-before against the fc7aad6 library (Mac and spark alike): idle batch of 16500 bars: 66000 driver points, 4 blocks of at least an eighth of the log, largest 14680064 bytes for a 7392000-byte log idle/burst batch, 16500 -> 66000 bars: holds 44040192 more bytes for 22176000 more bytes of driver points (1.986x) After: 1 block, largest 7509264 bytes; holds 22522528 more bytes (1.016x) for both hosts, the burst's history not growing with the batch. Its value half pins trades, trade digest, driver-point and command-event counts, native_continuation_hash() and broker_state_hash() for twelve pump paths (structural and projected driver logs, both label policies, synthesized paths, the after-calculation close, an aggregated script timeframe, a stream warmup), harvested on fc7aad6 with -DPINEFORGE_K24_HARVEST; identical on macOS clang and Linux gcc, and unchanged here. Identity battery (PERF0-K bin/identity.sh, 132 configurations, 3,000 bars): identical to fc7aad6 line for line; the Pine witness's broker_state_hash() stays 235509512418453834. Ruling (ADR-0001 rule 2): (a) the mechanism is generic -- a capacity policy over the kernel's own append-only logs, no platform branch; (b) no knob and no surface: no spec field, no hook, nothing hash-visible or readable moves. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit a02b08335f03c8e9de86ea8f2dc28a7aab645bdb)
… PERF-K1, witness) Canonical slot labels resolve each bar's input and script intervals through native_calendar, the session-day facts of every decision read the same calendar, and those intervals become the coordinates of every driver point the continuation digest folds. Lane PERF-K1 memoizes how the kernel resolves them; this row pins what they are before the lane. tests/test_native_calendar_hash_witness.cpp folds, in delivery order, every NativeInputContext, every NativeDecisionContext of a bar open, a bar and a print, every NativeTimeframeBarContext, then every driver row of native_events() and every closed trade, into one FNV-1a value per scenario, over twelve scenarios: a UTC year end, a masked America/New_York RTH session across the 2024-11-03 clock change with feed holes, an Asia/Tokyo lunch break, Europe/London's March change, a POSIX rule zone with an overnight session, a fixed offset, D over hourly input, a declared hourly series, FeedTolerant 5 -> 15, a FeedTolerant and a canonical stream across a closed night, and a print stream with quiet slots and a partial final slot. The continuation digest itself is not pinned: it folds the zone's tzdata content (E23), which differs between hosts that agree on every rule read here. The folded values depend only on the zones' 2024-2025 rules and exact binary prices: harvested with -DPINEFORGE_K1_HARVEST against the fc7aad6 library, identical on macOS arm64 (AppleClang 17) and Linux aarch64 (gcc 13.3), and the row passes against fc7aad6 on both. Kernel-only (source-free). No src change in this commit. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 0192aeba798ae36d22c3d89aaf6c87fe924c04e9)
…ERF-K1)
Canonical slot labels pay the calendar per bar: interval_containing
resolves the session day of the instant and of its last traded close, and
each resolution builds up to three fresh session days -- every declared
window resolved through the zone (mktime under the process timezone
lock), merged and copied -- behind an is_utc_zone string normalization and
a gmtime_r/localtime_r. PERF0-K measured 6.3 us/bar idle on UTC and
48-68 us/bar (spark) / 666 us/bar (macOS) on America/New_York.
src/native_calendar_memo.hpp (internal, not installed) adds
SessionDayMemo and memo-taking overloads of interval_containing (both
spellings), session_day_at and in_session. src/native_calendar.cpp
implements them statement for statement as their memo-free namesakes,
which stay verbatim as the reference:
* session_day_from_open is a pure function of the calendar value and
the open date: results live in a 64-slot table direct-mapped by the
date's day number and are handed out by address (no SessionDay copy);
* session_day_containing keeps the memo-free algorithm itself -- the
instant's local date, then open dates on it, the day before and the
day after, first holding day wins. The local date is gmtime_r's date
as integer arithmetic for a UTC calendar (no __tz_convert lock) and
one localtime_r per new whole second otherwise (8 kept);
* is_utc is decided once per memo.
It does NOT take the brief's "the cached day holds ms, so it is the
answer" shortcut: measured on macOS (deterministic mktime), that shortcut
answers another session day than the memo-free lookup for America/Sitka
and America/Juneau under "2330-2300" in October 1867 (261 and 288 of
33,311 instants), where cycles do not tile. Exactness therefore costs a
zoned calendar one localtime per new second; a UTC calendar pays integer
arithmetic only.
Also measured, pre-existing and unchanged: on glibc the memo-free
resolution itself depends on mktime's call history where a fold keeps
tm_isdst (America/Sitka 1867-10-18: 735 of 3,456 instants answer a day
apart after resolving 1867-10-15 vs 1867-10-21; Pacific/Kwajalein
1969-09-30 likewise); macOS's mktime is deterministic. Any memo, and the
brief's premise that session_day_from_open is a pure function, holds
exactly where the C library's mktime is.
Witness (fail-before / pass-after): tests/test_native_calendar_memo.cpp
compiled against fc7aad6 fails with "fatal error:
'../src/native_calendar_memo.hpp' file not found"; here it compares
interval_containing / session_day_at / in_session memoized vs memo-free
at 9,612 instants (walks across clock changes, year ends and session
breaks, random instants over 1972-2037, int64 extremes) in UTC,
America/New_York, Asia/Tokyo with a lunch break, Europe/London,
EST5EDT,M3.2.0,M11.1.0 and UTC+05:30 under five sessions and eight
timeframe pairings, plus America/Sitka October 1867 wherever the
reference has one answer: equal everywhere on macOS and Linux. A scratch
mutant taking the holds-shortcut fails it at 8 Sitka instants.
Ruling (ADR-0001 rule 2). (a) Mechanism: generic -- a cache of the run
calendar's own pure resolutions, for every host and zone; no platform
rule. (b) Knob: none; nothing observable, hashed or public changes (no
public header, no epoch). The consumer and the preflight adopt it in the
next commit.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
(cherry picked from commit 2bd693defd74dfff18c05dd1caa4abe2f2a03138)
… validity (R5 lane PERF-K1) Counted on a canonical-label idle run, the consumer and the preflight ask the memo for an interval 7 times per bar, 5 of them a repeat of one of the last 8 questions (a bar's input and script interval, the next bar's session-day facts, every driver point). An interval is a pure function of the calendar value, the instant and each timeframe's unit and count, so the memo now keeps the last 8 answered (newest first on lookup). Whether the calendar is valid is decided once per memo, as whether its zone is UTC already was, and the containing-day lookup hands the open date's day number to the next-span walk instead of recomputing it. The memo's calendar-identity check is inline. An invalid calendar or timeframe answers nothing before the kept intervals are read, exactly as the memo-free function does, so only valid pairs are ever kept. tests/test_native_calendar_memo.cpp now asks every walk's second half again, newest first, so answers the memo keeps and answers it resolves again are both compared: 12,672 instants equal, macOS and Linux. Ruling (ADR-0001 rule 2). (a) Mechanism: generic -- caches of pure calendar answers for any host. (b) Knob: none; no public header, value, hash or epoch moves. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit ef6ceefbadbca0a8b735f4931facf0fddd2f4fc6)
…rough the memo (R5 lane PERF-K1) NativeExecutionConsumer owns a SessionDayMemo over calendar_ (calendar_memo_, mutable like interval_cache_, never folded). reset_calendar_memos() forgets it together with F5's session_day_memo_ at both sites that rebuild calendar_ -- apply_spec and configure_native, so a configure that swaps the calendar mid-life starts from nothing -- and every calendar_ lookup passes it: input_interval_at, script_interval_at, session_point's day, a declared series' bucket interval, the stream in-session gap walks, the print path's slot intervals and the partial slot at stream_end. risk_day's session_day_ordinal is left memo-free: it runs only under a declared risk block and half the time on its own all-day calendar. No thread-local; the consumer's .cpp edits are line-neutral. preflight_native_inputs keeps a memo local to the call for the session it parses, so its canonical per-bar interval and the StreamWarmup successor check resolve the input's few session days once. Witness (fail-before / pass-after): tests/test_native_calendar_memo_cost.cpp counts heap allocations of one idle run under Canonical and under FeedTolerant labels and bounds the canonical excess at 0.25 per bar. Against fc7aad6 it fails: UTC 24x7 5m 83.98 extra allocations per bar, Asia/Tokyo 1m with a lunch break 224.26. Here: 0.02 and 0.02, macOS arm64 and Linux aarch64 alike. tests/test_native_calendar_hash_witness keeps every pinned value; the PERF0-K 132-configuration byte-identity battery on spark (3,000 bars) is 132 lines against 132 with an empty diff. Ruling (ADR-0001 rule 2). (a) Mechanism: generic -- the run's own calendar, cached for every host; nothing platform-shaped. (b) Knob: none; the memo is derived state, folded nowhere, and no public header, native API readback, hash value or epoch changes. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit e7893a3d46227fb497294860162e2727ac0b303a)
…e (R5 lane PERF-K1) The consumer's calendar memo is keyed to calendar_'s address, which a reconfigure keeps, so what invalidates it is reset_calendar_memos() where calendar_ is rebuilt (configure_native, and apply_spec at every begin). test_native_calendar_hash_witness now also runs a host on America/New_York, reconfigures it to Asia/Tokyo and runs the same tape again: bar for bar its intervals, coordinates and session-day facts must fold to what a fresh Asia/Tokyo host observes (event ordinals and the decision floor, which a reused host carries over, are left out). Passes against fc7aad6 and here; a scratch consumer whose reset_calendar_memos() leaves calendar_memo_ alone fails it (reused.fold.h == fresh.fold.h). No src change. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit ded474e7566a6a1c24d552d6276c0f09f87f7499)
…ic, not gmtime_r per point (R5 lane PERF-P5611, P5) Two per-point libc calls sat on every Pine run's path (PERF0-P: 59 ns/bar together, `gmtsub` 5.3 % inclusive on the 14 shared probes): - metrics::month_key_utc, which keys every equity-curve point of the monthly Sharpe / Sortino walk by gmtime_r's (tm_year + 1900) * 12 + tm_mon; - PineExecutionAdapter::chart_day_key on a UTC chart (no chart timezone, "UTC" or "Etc/UTC"), which the intraday day ledger reads every bar and the cap clock at every cap read, as gmtime_r's tm_mday * 100 + tm_mon + 1. Both now take the same fields from Howard Hinnant's civil_from_days on the floor day of the point's whole seconds (the millisecond stamp divided by 1000, truncated toward zero, exactly as before). The arithmetic is exact for every second within +/-2^40 of the epoch (about 34,800 years each way); outside that span both keep calling gmtime_r, so the keys cannot move anywhere, including the pre-epoch truncation month_key_utc documents. A zoned chart keeps chart_day_key's localtime_r path untouched. month_key_utc moves from the TU's anonymous namespace to pineforge::metrics::detail (external linkage, no header) so the witness can reach it; the walk calls detail::month_key_utc. Witness (fail-before, then pass): - tests/test_utc_month_key_arithmetic.cpp (kernel profile too): 12.1 M keys against the pre-lane body verbatim -- every hour 1970-2100 with a random millisecond, every day of years 1-9999 (and the millisecond before it), both sides of every month boundary 1900-2100, both sides of the 2^40 edge. Against fc7aad6: "Undefined symbols ... pineforge::metrics::detail::month_key_utc(long long)". After: ok. - tests/test_chart_day_key_arithmetic.cpp (source-bound): 15.3 M keys through the host's fixture read for "", "UTC" and "Etc/UTC" -- every hour 1970-2100, both sides of every day boundary 1969-2101, every day of years 1-1969 and 2101-9999, the 2^40 edge -- and a New York chart against localtime_r. It passes on fc7aad6 as well: it is the identity oracle. - Under AddressSanitizer both rows read every 15th day of the year 1-9999 sweep (every libc call is instrumented there); every other profile reads all of it, and the 1970-2100 sweeps run whole everywhere. Ruling (ADR-0001 rule 2): generic. A cost change inside the kernel's own report metric (engine_metrics.cpp) with no capability, spec field, hook, hash or readback added or moved; the adapter half is source-layer code. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 13b023e9e50b04f740ce9259297d05ec85006fe0)
…literals without a parse, in one definition (R5 lane PERF-P5611, P6) PineStrategyHost's per-bar callbacks (on_native_bar, on_native_applied, on_native_tick, the closed-lot excursion) and PineScheduler::recalculate each asked aggregates_input_bars of the run's spec, 1.04-1.40 times a bar (PERF0-P), and each copy -- one per translation unit -- answered it by tf_ratio(input_tf, script_tf): two tf_to_seconds parses per ask, 18 ns/bar. The spec cannot change inside a run, so the answer is a per-run invariant. It is not stored: PineScheduler and PineExecutionAdapter are by-value members of PineStrategyHost, the generated strategy's base, so a member would move the script ABI layout (and their headers are not this lane's); a thread_local is ruled out by the brief; and no begin-time local reaches a kernel callback without storage. The spec answers it instead, cheaply: - equal literals -- every chart that does not aggregate, and the shape the adapter projects whenever the script timeframe is the input's -- need no parse. tf_ratio(x, x) is -1 exactly when tf_to_seconds(x) is negative, and a configured spec's literal passed native_calendar::parse_timeframe (validate_native_run_spec: [1-9][0-9]*[SDWM]? or a bare D, W or M), so one of at most four characters cannot overflow that int product and is negative only when it is monthly: the answer is script.back() == 'M'; - every other pair (unequal, or equal and longer) keeps tf_ratio, so the answer cannot move for any spec a run can hold. The two copies become one definition, source::detail::aggregates_input_bars in pine_strategy_host.cpp (external linkage, no header); pine_scheduler_native.cpp declares it where its copy was. Witness (fail-before, then pass): tests/test_aggregates_input_bars_literals.cpp (source-bound) judges the definition against the pre-lane body verbatim over every ordered pair of 553 literals parse_timeframe admits (every suffix at the counts where the unit arithmetic changes shape, each unit's largest count whose seconds fit an int, 400 seeded random counts), equal pairs, the undetected view and a view without a spec: 306,365 pairs. Against the library without this change: "Undefined symbols ... pineforge::source::detail::aggregates_input_bars(...NativeStateView const&)". After: ok. No kernel change (ADR-0001 rule 2 not engaged). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> (cherry picked from commit 651b05122d07f5e0ec1198ab6506ffedb656d558)
…- UTC by civil arithmetic, other zones from memoised offset intervals (R5 lane PERF-P5611, P11)
decompose_ms_local (src/session_time.cpp) feeds hour() / minute() /
second() / dayofmonth() / dayofweek() / month() / year() / weekofyear(),
the session filters (passes_session_filter, session_in_pre/postmarket) and
session_trading_day_open_ms. Each call was gmtime_r for a zone that
normalizes to UTC, and otherwise localtime_r inside tz_util::ScopedTimezone:
the process mutex, two normalization strings and, whenever the previous
read was in another zone, setenv + tzset (KI-35). PERF0-P: 43 ns/bar mean
on the 14 shared probes, heavier on time-filter strategies.
Now:
- UTC (the plain spellings "", "UTC", "Etc/UTC", "GMT", "Etc/GMT" without a
parse, any other spelling by its cached normalization) is Howard
Hinnant's civil_from_days on the floor day, every gmtime_r field
(the nine ISO fields, tm_gmtoff 0, tm_zone libc's own UTC text), for
every second within +/-2^40 of the epoch; gmtime_r keeps the rest.
- Another zone reads a per-thread memo of offset intervals: [lo, hi] over
which localtime_r gave one (tm_gmtoff, tm_isdst, tm_zone) and whose
fields are the civil fields of the second plus tm_gmtoff. A stamp inside
one costs no libc call, no process lock and no TZ switch. A stamp
outside costs what it always did -- localtime_r under ScopedTimezone,
whose answer is returned unchanged -- and opens a one-second interval; a
stamp within 48 h of a same-state interval joins it once localtime_r
confirms the state every 12 h across the gap, and the interval then
reaches up to 30 days on in the direction the reads moved, stopping at
the exact transition second (bisected). localtime_r has therefore
answered a second within 12 h of every second an interval holds, so it
can only miss a pair of transitions closer than 12 h that restore the
same offset, flag and abbreviation -- tzcode zdump's own sampling
assumption (zdump(8), LIMITATIONS); the shortest gap between two
transitions of one zone in this host's tz database (2026c) is 6.9 days
over 1970-2100. A zone whose libc fields are not civil-plus-offset
(leap-second "right/" zones) is never memoised. 8 slots per thread,
least recently read evicted; fixed-size, trivially constructed
thread_local storage (no allocation, no destructor).
- The UTC month open (gmtime_r) and the UTC D / W / M period close
(localtime_r + mktime under ScopedTimezone("UTC"), where mktime is civil
arithmetic) are computed the same way; neither switches the process TZ
any more. Every other zone keeps its mktime path.
- The UTC tm_zone is libc's own text (gmtime_r's), read once into an
idempotent atomic cache, not a guarded static: a sanitizer build pays
for the guard on every call.
- The UTC checks of the calendar opens ask the same cached normalization
instead of building the string per call. Exceptions are unchanged: a
spelling the memo does not hold is normalized exactly as before.
decompose_ms_local gains external linkage (no header) for the witness.
Per call (Mac M4 Max, -O2 static driver, 15m walk, best of 5; load 15-17;
PERF-P5611-scratch/mb.cpp), base fc7aad6 -> this commit:
local_hour UTC 41.6 -> 10.2 ns
local_hour America/New_York 239.3 -> 13.2 ns
hour + minute NY, dayofweek Tokyo, per bar 111067.3 -> 40.5 ns
passes_session_filter 0930-1600 NY 285.8 -> 58.1 ns
timeframe_time 15, session, NY (7-arg) 292.9 -> 67.8 ns
timeframe_time_close D, UTC 1382.7 -> 26.4 ns
timeframe_time M, UTC 55.0 -> 31.6 ns
Witness (fail-before, then pass), both kernel-profile rows:
- tests/test_local_time_fields.cpp: 23.7 M field-by-field comparisons with
the pre-lane body verbatim (nine ISO fields, tm_gmtoff, tm_zone text):
UTC spellings hourly 1970-2100 and daily 1900-2100 plus the 2^40 edge;
every transition 2000-2100 of 38 shape zones (5,061 transitions: the
seconds around each, a 72 h walk at 15 min forward and at 15 min 7 s
back); every minute of the corpus feed's span (2020-01-01 ..
2026-05-04) in America/New_York, Asia/Taipei, Europe/London, Asia/Tokyo
and UTC; seeded random stamps 1900-2200 per zone; ten zones in turn on
one thread; fixed offsets; and the UTC month open and D / W / M close
against their pre-lane bodies. Against the library without this change:
"Undefined symbols ... pineforge::decompose_ms_local(long long,
std::string const&, tm&)". After: ok.
- tests/test_local_time_lock_free_hit.cpp: a worker reads two New York
stamps a minute apart, then reads a third between them while the main
thread holds the process timezone lock. Against the library without
this change the read blocks until the main thread gives up (5 s):
"FAIL tests/test_local_time_lock_free_hit.cpp:112 returned". After: ok,
with libc's hour. A UTC read is the control (it never took the lock).
- Under AddressSanitizer the fields row's bulk sweeps (UTC spellings,
corpus minutes, calendar paths) read every 15th stamp; the transition,
random, zone-turn and fixed-offset sections run whole (spark, gcc 13
ASan+UBSan -O0: 99 s, 4.6 M comparisons, 5,187 transitions).
Ruling (ADR-0001 rule 2): generic. A cost change inside the kernel's own
calendar functions with no capability, spec field, hook, hash or readback
added or moved; the functions' inputs stay data (a timezone, a session, a
timeframe) and every value is libc's.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
(cherry picked from commit 279fa1e13aec57fd854a8d3a37b8c79a9e4d1156)
Lane PERF-K3's 74d61797 rewrites the three-line comment its step 1 (4adb1776) put above test_native_match_hash_witness and adds the two other K3 rows under the new five-line comment. Picked after P23 and P4, whose rows sit directly above, git applied the added lines and silently kept the three it should have removed: no conflict, two PERF-K3 comments in a row. This drops the stale one, so the block reads as it does at r5/perf-k3; a sequential merge of the seven lane tips (git merge-tree, base fc7aad6) agrees with the picked tree everywhere outside docs/ once it is gone. Comment-only; the registered rows are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…cached PineStrategyHost view Lane PERF-P23 routed every adapter downcast of the bound host through pine_view, the per-thread memo of dynamic_cast's answer keyed by the host's address, complete object and dynamic type (its report: "If P4 adds adapter downcast sites, they should go through pine_view"). Lane PERF-P4, built on the same base, reached the consumer at its three in-place receipt reads -- follows_same_bar_declined_reversal, revive_brackets_after_margin and observe_terminal_receipts -- through a new inline static accessor, PineStrategyHost::adapter_receipt_consumer, which static_casts the host and hands out a const IExecutionConsumer&, so each site const_cast it back. The three sites now read pine_view(&host)->execution_consumer(). The view is a non-const PineStrategyHost*, so the non-const execution_consumer() overload answers and no const_cast is left. Each site stays behind P4's own guard (both receipt readers installed), which only PineStrategyHost's begin satisfies on its own adapter, so the view is never null there and is the same object the static_cast named. P4's in-place visit (visit_commands_after), its cursor rule and the terminal watermark rule are untouched. The accessor has no caller left and is deleted, which returns include/pineforge/source/pine_strategy_host.hpp to its fc7aad6 text: slice A now changes no public header at all. No value moves: the same consumer is visited in the same order with the same cursor. Pinned by P4's test_adapter_receipts_in_place (cursors, recorded broker-state hashes, trades, the commandless read's zero allocations) and P23's test_adapter_host_view_memo, both unchanged. ADR-0001 rule 2 is not engaged: adapter-internal, no hook, hash input, readback or layout change. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…mber does since P4 Lane PERF-P4's finding 2. The comment still said the member is a "derived receipt watermark used only to avoid materializing an owning native-events snapshot when no terminal command was appended". Since P4 the no-new-event early return reads the consumer's event high water and no longer consults this member; what is left is what the comment now says: it takes the consumer's terminal-receipt high water as observe_terminal_receipts last read it, only on a run without an intrabar path (0 under the magnifier), and it only feeds the broker-state hash (pine_state_hash.cpp). The member stays: removing it would move hash values, which is an epoch and the owner's call. Comment-only, proven two ways: strip_comments.py gives the same digest for the header at the previous commit and here (9bc285986180287c), and the 86 objects of pineforge and pineforge_kernel rebuilt with the new header (8 recompiled) are byte-identical to the ones built before it. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…24 and K1 made eleven times cheaper test_native_bare_host_contracts (R5 lane F3, item 2) witnesses that a per-bar continuation read no longer re-walks the spec's bar arrays: 2 000 script bars over a 10 000-bar lower-timeframe path, read every bar, must cost under 3x the same run read once. It fails on the merged tree only: continuation read every bar 0.0072 s, read once 0.0022 s, ratio 3.3-3.5 The reads did not get dearer. Each still costs about 2.5 us (every-bar minus once is 0.0050 s at fc7aad6, at every lane tip and here). The run they are divided by got cheaper, and the lanes' gains stack on it: read once is about 0.024 s at fc7aad6 and at the P1/P23/P4/K3/P5611 tips (ratio 1.2-1.3), 0.016 s at the K24 tip (lower feed by binary search, ratio 1.3), 0.010 s at the K1 tip (calendar memo, ratio 1.5), and 0.0021 s with both (ratio 3.4 in Release, 1.6 in Debug). expectation corrected: ratio < 3.0 -> ratio < 20.0, because K24 and K1 together made the read-once run about 11x cheaper while each read costs what it did. The guard still catches what it exists for: with hash_spec forced to ignore the cached digests (scratch mutation, every read re-walks the 10 000 path bars) the same tree measures 440-468x, and the row fails at the new bound (CHECK FAIL ...:594 ratio < 20.0). The comment gives the numbers. No value moves; the digest equality check is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…29, release 586 -> 607
Recounted on the integrated tree from the rows that run (ctest -N in each
profile's own build dir), not summed from the lanes' reports:
build-ci-release Total Tests: 607 (586 + 21; no release row skips)
build-ci-kernel Total Tests: 230 (214 + 16; 229 run, the WebSocket
row still skips on a system libcurl)
build-ci-debug Total Tests: 588 (567 + 21; this profile has no floor)
Per lane, release / kernel: P1 2/1, P23 4/3, P4 2/1, K3 3/3, K24 2/2,
K1 3/3, P5611 5/3 -- 21/16, exactly what the seven reports claimed. The
comment above each floor names every row.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Digits only: with every anchor's digits blanked, all six pages are byte-identical to the previous commit and keep their line counts. check_doc_anchors: 1631 of 1631 anchors resolve (276 did not after the picks); doc lint: no stale counts or other offenders. The six lanes' own mechanical re-anchor commits were not picked (they conflict with each other digit by digit), so the pages carried the digits of fc7aad6, except the ones lane PERF-P1's picked docs commit wrote against its own tip. Each citation was carried from the tree its digits were written against: - 323 claimed citations through an exact difflib line map of the cited file, origin -> this tree (origin = fc7aad6 unless P1's commit wrote that token, then r5/perf-p1), each kept only where check_doc_anchors' own judge() passes at the new span. This agrees with every move check_doc_anchors --fix proposed (79 of 83 exactly; 3 ranges one line longer, because a line was added inside them; 1 range that --fix would have started at its symbol, 12 lines in, now carried whole) and with all 135 reanchor_doc_citations.py moves over the base and the seven lane tips; of the 49 citations that tool left in conflict between trees, each takes its origin tree's reading. - 42 citations with no claim the gate can check (bare pine_adapter.cpp / pine_strategy_host.cpp / native_execution_consumer / pine_scheduler_native.cpp / engine_metrics.cpp lines), carried only where both cited lines survive the map unchanged, as K3, K24 and K1 carried theirs. - 7 by hand: the floor citations (KERNEL_MIN_TESTS ci_verify.py:161, RELEASE_MIN_TESTS ci_verify.py:217) in CONTRIBUTING.md and contributing-llm.md, and native-feature-parity AG1's aggregates_input_bars, whose definition lane PERF-P5611 moved into the one source::detail definition (pine_strategy_host.cpp:134), as that lane's own re-anchor set it. check_doc_anchors --fix and reanchor_doc_citations.py (--ref fc7aad6 and the seven lane tips) then find nothing left to move. The carrying script is kept as exec/tools/int17_exact_reanchor.py. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…form through one allocator (INT17b) The sanitizers lane of PR #279 aborted two lane PERF-P4 rows, test_native_command_history_read and test_adapter_receipts_in_place: ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new vs free) #1 operator delete(void*, unsigned long) tests/test_native_command_history_read.cpp:66 allocated by: operator new(unsigned long, std::nothrow_t const&) (asan_new_delete.cpp) Each test replaced only the plain/array operator new and the plain/sized operator delete, to count allocations. libstdc++'s temporary buffers (std::inplace_merge in NativeExecutionConsumer::events_after, std::stable_sort in PineExecutionAdapter::apply_open_market_admission) take their memory from the NOTHROW operator new -- left as ASan's own -- and give it back through the SIZED operator delete, which the test had routed to free(). Two allocators in one executable; ASan stops at the mismatch. tests/global_allocation_replacement.hpp is now the one replacement set: all twenty replaceable forms (plain, array, nothrow, sized, aligned and aligned nothrow, for new and for delete) go to one allocator, std::malloc (posix_memalign past the fundamental alignment) released by std::free, with the standard new-handler loop and nothrow semantics. A test reads and sets plain state: `allocations` (every allocation, measured as a difference around a span), `refusing` / `refused` (every form fails the way it fails, nothing allocated), and, opted in with PINEFORGE_TEST_ALLOCATION_BYTES, per-block size headers for a recording window (`recording`, `live_bytes`, `on_recorded`). Every test that replaced the global functions now includes it instead of its own partial set -- all of them were incomplete (no aligned forms; most had no nothrow forms either), so a libstdc++ path through the missing forms would have mismatched the same way: test_native_command_history_read P4 counted, was plain+sized only test_adapter_receipts_in_place P4 counted, was plain+sized only test_native_calendar_memo_cost K1 counted, no aligned forms test_native_batch_log_presize K24 byte window, no aligned forms test_native_order_resting_core counted, partial aligned/nothrow test_native_selected_core counted, plain+sized only test_l10ae_allowance_refresh_liveness counted, plain+sized only test_l8b_pending_mirror refused, no aligned forms test_market_admission_causality refused, plain+sized only test_native_run_spec refused, plain(+sized) only test_pending_order_mirror_no_alloc_l4d refused, plain+sized only; its base body keeps its two names, now references to the shared state The uncompiled legacy base tests/test_pending_order_mirror_no_alloc.cpp (REMOVE_ITEM'd from TEST_SOURCES; its ab9714b body is the twin's parity reference) is left as it is. No assertion, bound or pinned value changes: the counted windows read the same numbers (calendar memo cost: canonical 159, tolerant 88, 0.02 extra per bar; batch presize: 22522624 held for 22176000 recorded, 1.016x; the P4 rows 57356 and 98 checks), and nothing under src/ or include/ changes. Proof, the sanitizers profile reproduced on spark (Linux aarch64, GCC 13.3.0-6ubuntu2~24.04.1): ci_verify's own configure argv and ctest flags, the six ABI providers prepared, the job's ASAN_OPTIONS/UBSAN_OPTIONS. before (f71cd82): both rows abort with the report above, exactly as CI; after, same options: the other ten converted rows pass, including test_adapter_receipts_in_place (96.6 s); the whole suite, with detect_stack_use_after_return=0 added (aarch64 GCC 13's fake stacks cost 30-130x per row on that host: 15.56 s against 0.12 s for one row that CI's x86 runs in 0.51 s): before 99% tests passed, 2 tests failed out of 588 (the two rows) after 100% tests passed out of 588 Mac release, debug and kernel builds: every converted row passes. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Performance slice A of the R5 speed recovery: seven byte-identical lanes, integrated. No value moves — trades, every continuation / broker-state hash value, every readback, every public layout and the C API count are unchanged, and there is no epoch.
What
native_eventsIntegration: P4's read sites go through P23's cached
PineStrategyHostview (the extra accessor is gone), theterminal_receipt_cursor_comment says what the member does now, the docs' 1,631 anchors are re-anchored once (digits only), and the ctest floors count the new rows (release 586 → 607, kernel 213 → 229). One expectation changed: F3's linear-read witness boundratio < 3.0 -> < 20.0, because K24 and K1 made its read-once denominator about 11× cheaper while each read costs what it did; a mutation that disables the digest cache still measures 440–468× and fails it.Speed (merged vs
fc7aad62)ab9714be.063e4460(the engine behind the published "15× vs PyneCore"): 0.689–0.786× per bar, i.e. an estimated ~19–21× vs PyneCore on spark's ratio (up to ~28× on the Mac's); the benchmark re-time will state the real number.Proof
9182e3d(+ the 30-probe subset); the 132-configuration kernel identity battery and the 200-row Pine fingerprint sweep are identical tofc7aad62; witnessbroker_state_hash=235509512418453834on063e4460,fc7aad62and this tree.ci_preflight(25 stages),ci_verifyrelease (607 rows) / debug / kernel (229 rows, residuals 0 findings), Doxygen 0 warnings.e6a64cd):exp-r5-int17-perf-a-20260923, 72/72 cases, candidate17356a3d…, 4,190 graded, 0 tier or outcome changes, coverage lost 0, offline band1 +0/−0 band2 +0/−0; pr_gatepineforge-pr-gate-t59mcneutral (target.not-positive), accepted at net 0.🤖 Generated with Claude Code
https://claude.ai/code/session_01LifAW9e5J6gFx7gatuxETE