Skip to content

Session clock: read each day's occurrences once per bar; time() arguments resolved without copies - #339

Merged
luisleo526 merged 1 commit into
mainfrom
px/perf-vcp-final
Oct 5, 2026
Merged

luisleo526 merged 1 commit into
mainfrom
px/perf-vcp-final

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

What

time(tf, session, tz) with a script's own session argument reads TradingView's session clock outside UTC (#334). That path asked the per-day session cache about nine times a bar, and copied the session and zone strings on every time() / time_close() call. This PR reads each day's occurrences once per call, caches utc_zone per session-cache entry, and resolves the call's strings by reference. It changes one file, src/session_time.cpp: the language calendar functions under rule 1 of ADR 0001. It adds no switch, input, API or hashed state.

Value identity (on this tree, against main)

  • the old and new session functions run side by side over the whole suite: 0 mismatches;
  • a differential over generated sessions, zones and timeframes, walking every DST switch of 2024–2026 minute by minute: 16.6 M queries, identical digest;
  • an independent review repeated it with 33.2 M queries: 0 different answers, identical cache and zone-memo history, clean under ASan + UBSan;
  • the 30 witness harvests: byte-identical, 0 re-pins;
  • the whole-suite per-trade dump (all 21 Trade fields at %.17g, every state hash): identical apart from rows that differ between two runs of one tree;
  • all 307 tape and witness executables: byte-identical;
  • corpus 312/312, 0 moved.

Timing (on this tree, against main 44eab7b)

Measured on a Xeon 8375C with 4 independent builds per tree, paired rotating blocks (81 per shape), and every process under exactly 22 environment variables. Positive means slower; * means the 95% interval covers 0. The padded control is this commit with every function outside session_time.o placed at main's address, checked symbol by symbol.

native ta-sma bracket udt composite-vcp
batch median, ungathered (1 m / 1 m) +1.23 +1.26 +0.87 +0.83 −9.54
— padded control +0.03* +0.06* +0.02* −0.00* −7.93
per call, ungathered +0.21 +0.04* +0.19 +0.10 −5.16
— padded control +0.05* +0.01* −0.02* −0.02* −4.84
stream p95, ungathered +0.46 +1.53 +0.46 +0.17 −3.07
— padded control +0.04* +0.03* −0.05* +0.01* −3.46
batch median, gathered (1 m / 3 m) +0.60 +0.09* +0.54 +0.41 −5.80
— padded control +0.06* +0.06* +0.05* +0.09* −5.25
  • Only composite-vcp's work changes. Its instruction count drops from 232.5 M to 220.1 M over 2 × 5,000 stream pushes, and from 2,512 to 1,275 per timeframe_time call.
  • The other four cases execute identical instructions. Their differences against main are code placement: this file shrinks by 263 bytes and moves the hot functions by 192–256 bytes. With the layout held fixed (padded control), all four read 0 within noise on every metric. The commit message's table was measured with Pine adapter: recover the per-bar cost of the margin, sizing and admission rules #336's tree as the base; the table above is this tree.

Verification

  • Engine verify bundles on this exact tree: release 859, sanitizers 837, debug 840, kernel 307 with 0 boundary findings, parity 312/312, subset, docs.
  • One parity sweep of the full population on this head, paired with codegen main, is neutral: 7,989 comparable, no moves.
  • An independent review returned MERGE; its message fixes are applied.

🤖 Generated with Claude Code

…ents resolved without copies

time(tf, session, tz) with a script's own session argument reads
TradingView's session clock outside UTC (clock_intraday_bar, #334). Its
scans asked arg_day_occurrences at every read: about nine cache lookups a
bar through arg_cache_day, for at most five days, and utc_zone at every
call. And every time() / time_close() call copied the session, its zone
and the calendar zone into local strings: two heap allocations a call for
a zone name one past the small-string buffer ("America/New_York").

- clock_intraday_bar asks each day's occurrences of the cache once per
  call and keeps the vector; a day whose ring slot already holds it
  resolved is read from the slot (resolved_occurrences), any other through
  arg_day_occurrences as before. A repeated read returned the same vector
  and changed nothing: resolving a day touches only its date and the dates
  next to it (arg_day_switch of a clock window's start and end), so the ten
  days one call can touch never share a slot of the 64-day ring; and each
  day is first read at the same point, in the same order, as before.
- utc_zone(tz) is asked once per session-cache entry (arg_cache_utc), at
  the same place in the same condition: it has no side effect and depends
  on the spelling alone (both zone-memo claim sites record
  normalizes_to_utc of the spelling).
- resolve_session_tz and the timeframe default name the call's own strings
  or constants by reference instead of copying them.

Same answer for every call, same cache and zone-memo history; no hashed
state, switch, input or API. One file, src/session_time.cpp: the language
calendar functions of ADR 0001's rule 1, called only by the Pine host and
generated code.

composite-vcp, 2 x 5,000 stream pushes (callgrind): 247.8 M instructions at
a80c623 (#336), 242.4 M at f3bf9f0 (before the session clock), 235.4 M here;
timeframe_time 2,512 -> 1,275 instructions a call. The other benchmark
strategies never reach this code (identical counts).

Values: a80c623's code run beside this one at every call over the whole
ctest suite (0 differences in 28,164 clock scans, 128,560 repeated reads,
3.2 M argument resolutions); a differential over generated sessions, zones
and timeframes, every switch of 2024-2026 walked minute by minute
(16.6 M queries, identical digests); the whole-suite per-trade dump (all 21
Trade fields at %.17g, every broker and stream state hash), the 30 witness
harvests (0 re-pins) and 307 tape and witness executables byte-identical;
corpus 312/312, moved 0; release ci_verify with the receipt rows
passes, 859/859 (a first run lost one timing-ratio row,
test_native_intrabar_lower_lookup, to parallel load; it passes alone in
both trees and in the re-run).

Timing, measured with a80c623 (#336) as the base; this commit carries
the same patch onto main, and the pull request re-times it there.
c6i.8xlarge (Xeon 8375C), GCC 13.3, CPU pinned, 4 independent
builds per tree, rotating paired blocks (61 ungathered, 41 gathered), every
process under exactly 22 environment variables; percent, positive =
slower; 95% intervals within 0.28 points (batch median), 0.12 (per call),
0.46 (p95); * = covers 0.

  percent vs                 native  ta-sma  bracket    udt    vcp
  batch median, ungathered (1 m input, 1 m script)
    14b05c9                 -4.22   -2.99   +0.35   -1.70  -2.61
    f3bf9f0                 -8.90   -3.26   +1.07   -4.67  -2.02
    a80c623 (#336)          -2.88   -1.02   +0.10   -2.10  -5.11
  per call (stream mean), ungathered
    14b05c9                 -3.94   -0.84   -0.31   -1.32  -1.06
    f3bf9f0                 -7.22   -2.00   -1.35   -2.93  +0.01*
    a80c623 (#336)          -1.24   -0.34   +0.37   -0.56  -2.58
  stream p95, ungathered
    f3bf9f0                 -7.28   -1.75   -1.26   -3.34  +3.19
    a80c623 (#336)          -2.32   -1.30   -0.09   -2.31  -0.17
  batch median, gathered (1 m input, 3 m script)
    14b05c9                 -6.02   -4.09   -3.19   -3.48  -3.84
    f3bf9f0                 -9.48   -4.82   -3.09   -5.80  -1.68
    a80c623 (#336)          -0.88   -2.03   +0.16   -0.84  -3.63
  per call, gathered
    f3bf9f0                 -6.69   -2.00   -3.77   -3.63  -0.67
    a80c623 (#336)          -0.08*  -0.14   +0.25   -0.35  -1.66

native, ta-sma, bracket and udt execute a80c623's exact instructions;
their differences against it are code placement. This file's code is 263
bytes smaller, and the linker places every object's unlikely section ahead
of all hot code, so nearly every hot function moved by 192-256 bytes. A
control build (not this commit) that pads the object back to a80c623's
section sizes puts the hot functions at its addresses: bracket
then reads +0.08* per call against it, and composite-vcp -9.1
batch median, -4.5 per call and -3.4 p95 (ungathered).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@luisleo526
luisleo526 merged commit 52292db into main Oct 5, 2026
17 checks passed
@luisleo526
luisleo526 deleted the px/perf-vcp-final branch October 5, 2026 03:52
luisleo526 added a commit that referenced this pull request Oct 5, 2026
* docs: release notes and version scoping for v1.2.0

CHANGELOG: the Unreleased section becomes 1.2.0 (2026-10-05), a minor
release that adds the compiled execution-capabilities extension to the C
ABI (PF_CAPABILITIES_API_VERSION, strategy_capabilities_api_version,
strategy_capabilities_receipt). One bullet per change since v1.1.0 with
its PR (#328, #330, #331, #332, #333, #334, #335, #336, #337, #338 and the
session-clock change, written #VCP until its PR number exists), the
recipe additions under State hashes (#328, #330, #335; domain tags and
epoch unchanged), Report keys (docker/run_json.py is byte-identical to
v1.1.0's, so no key changes) and Migration.

Version scoping: README, install and public-contract pages name v1.2.0
and codegen 1.2.0; "since the next release" and "fixed on main" in the
capabilities guide and the runner guide become 1.2.0 and 1.1.0 (#325).
Lines that state what an earlier release shipped stay.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* docs: 1.2.0's grades from the facts tokens, #339, and codegen's license

- CHANGELOG: the session-clock change is #339 (merged as 52292db), in place
  of the #VCP token of 3082fbd; the "What a script author sees" bullet of
  3082fbd now states release 1.2.0's grades (registry baseline
  pineforge-parity-baseline-20261005-engine-52292db9: 7,970 excellent / 19
  strong of 7,989, none below strong) instead of the main scoreboard at
  6b77f06.
- README: the release sentence names 1.2.0 and renders releases[1.2.0], and
  the main scoreboard renders the same baseline, from pineforge-release
  facts/facts.json at 0c2f6838 (the facts export for the 1.2.0 release).
- README, LEGAL.md, docs/pages/index.md: pineforge-codegen is under the
  PineForge Source License 1.1 from its release 1.2.0; releases up to 1.1.0
  keep the PolyForm Noncommercial terms they shipped with.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant