SP0.13a (#749): gate findNextReservationTarget on reservation-eligibility (~9% fast-sim win)#754
Merged
Merged
Conversation
ShuntingLoop.toBlockInputObservation resolved BlockInputObservation.toSeparatorName by calling PathReservationService.findNextReservationTarget for every block input on every tick, including inputs that provably cannot take a forward reservation. The call is a BFS (findNextSemaphoresVia) plus a full topological-path enumeration per candidate (isPathAvailable -> findAllTopologicalPaths). Measured on vyhybna: 906 calls per `example shuntingLoop 300`, of which 892 (98.5%) are discarded unread by RuleBasedDispatcher.checkInput, which already returns null for FREE, not-approaching and already-extended inputs. Cost: 30.3 ms of 342 ms wall (~9%). Gate the search on eligibility. The three facts it needs (isApproachingThisInput, pathSetUpTowardThisInput, pathAlreadyExtendedBeyond) are already computed in the same function, so the gate is free. This narrows the sensor-port contract: toSeparatorName is now populated only where a forward reservation is possible, and is null elsewhere without the search having run. RuleBasedDispatcher behaviour is unchanged by construction. Documented on BlockInputObservation so a future LLM dispatcher does not read null as "track ahead is occupied". Native linuxX64 release, example shuntingLoop 300, median of 7: before 341 ms after 318 ms Golden output byte-for-byte identical (90/90 event lines, 5 trains, 211.1 s sim time). ShuntingLoopReservationTargetLazinessTest locks the contract and fails 2/2 without the gate (a FREE input resolves k1->doA1=A). Gates: core:jvmTest 2194, core:integrationTest 725, core:heavyTest 1001, dispatcher-agent:test 51, desktop-ui:test 650, desktop-ui:integrationTest 247 — all green. purity/detekt/ktlint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bedaHovorka
commented
Jul 11, 2026
bedaHovorka
left a comment
Owner
Author
There was a problem hiding this comment.
@copilot : some tips for clean code, please fix it
Contributor
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.
Closes #749.
Split out of #738, which is now closed as not-reproducible — the claimed 0.2 s → 0.6 s regression does
not exist in any release build (see the close-out
for the full bisect). What the investigation did find is this: a real, measurable ~9% of
fast-simwalltime spent on graph searches whose results are thrown away.
The problem
ShuntingLoop.toBlockInputObservationresolvedBlockInputObservation.toSeparatorNameby callingPathReservationService.findNextReservationTarget(to)for every block input on every tick — includinginputs that provably cannot take a forward reservation.
That call is a BFS (
findNextSemaphoresVia) plus a full topological-path enumeration per candidate(
isPathAvailable→findAllTopologicalPaths). Measured onvyhybna,example shuntingLoop 300:findNextReservationTargetcallsRuleBasedDispatcher.checkInputalready returnsnullfor exactly those inputs —FREE -> null,!isApproachingThisInput -> null,pathAlreadyExtendedBeyond -> null. The value was computed and thendropped on the floor.
The fix
Gate the search on eligibility. The three facts it needs are already computed a few lines above, so the gate
costs nothing:
Sensor-port contract change (deliberate, documented)
This narrows the
BlockInputObservationcontract:toSeparatorNameis populated only where a forwardreservation is possible, and is
nullelsewhere without the search having run.For
RuleBasedDispatcherthis is behaviour-preserving by construction. For a future LLM dispatcher thedistinction matters, so it is now spelled out in
BlockInputObservation's KDoc: anullmeans "noforward-reservation target applies", not "the search found nothing" — a dispatcher must not read it as
evidence that the track ahead is occupied. Both cases call for the same response anyway (no reservation this
tick).
Results
Native
linuxX64release,example shuntingLoop 300, median of 7:c3eb0f66(pre-seam baseline)The seam's per-tick overhead is now back inside baseline noise.
Golden output is byte-for-byte identical — 90/90 event lines, 5 trains, 211.1 s sim time.
Tests
New
ShuntingLoopReservationTargetLazinessTest(:core,@Tag("integration-test")) locks the contract:no non-eligible input may carry a
toSeparatorName, and aFREEinput never carries one. Verified tofail 2/2 without the gate (
k1->doA1=Aresolves), and pass with it.:core:jvmTest:core:integrationTest:core:heavyTest(required — touchessim/):dispatcher-agent:test:desktop-ui:test:desktop-ui:integrationTestcheckCoreCommonMainPurity/detekt/ktlintCheck0 failures across all suites.
Not in this PR
The investigation found that ~60–70% of fast-sim wall time is kDisco RKF45 continuous integration
(1.85M
Motor.derivatives()calls/run, caused bydtMax = 1e-3acting as the event-detection resolution).That is a much larger and much riskier change — filed as #750 (model side) and
kdisco#67 (engine side). Also spawned: #751, #752, #753.
🤖 Generated with Claude Code