Where it lands
Decode or execute in SQL (sqlcpu)
What you are trying to do, and what stops you
Native mode runs P_Ticker in SQL. The player walks, slides, reaches for
lines, opens doors and drives plats, floors and switch timers. Two things
stop the parity run going further, and one of them is a cost.
The thinkers are not written. demo3 agrees with the reference emulator
for every gametic from 2 to 205. At 206 the player's momentum parts, because
by then the engine has woken a monster, spawned what it fired and pushed the
player with it. What is missing is the round body over the thinker list:
P_MobjThinker and the state cycle, A_Look and A_Chase, the attack
family, P_DamageMobj and P_KillMobj, P_SpawnMobj and the slow path.
P_MovePsprites goes with them: five fields are already open at gametic 2
(m_frame, m_tics, m_state, m_lastlook, psp_sy) and they are all
waiting on a thinker.
Until they land, nothing downstream can be checked against the trace.
demo3 starts a plat at gametic 603 and a second door at 707, and neither
is reachable while the player is somewhere the engine did not put them. The
plats, floors and buttons that exist are tested against readers written from
p_plats.c and p_spec.c with the thinker seeded into a state row, which
is a real check but a weaker oracle than the emulator.
The activation path is only half there. P_UseSpecialLine dispatches the
manual door specials to EV_VerticalDoor. The switch specials that reach
EV_DoPlat, EV_DoFloor, EV_DoDoor, EV_DoCeiling, EV_BuildStairs and
EV_DoDonut are not written, nor is P_ChangeSwitchTexture, nor
P_CrossSpecialLine for the walkover triggers. A press that reaches one of
those leaves the tic unresolved, which is honest but is not the engine.
The tic costs 54.7 ms against a budget of 20. That is with doors, plats,
floors and buttons in, measured over the 120 tics that include the door
moving, on an otherwise idle machine. It was 506 ms when the clip sat inside
the per-thinker fold.
Where the time goes, measured over gametics 2 to 61 where no plane moves and
the clip's input array is empty on every tic:
| build |
per tic |
| before the plane pass |
16.4 ms |
| the pass with the clip stubbed out |
25.0 ms |
| the clip's inputs built, its lambda body a constant |
34.6 ms |
| the whole of the wide clip |
52.2 ms |
the clip narrowed to P_ThingHeightClip |
49.1 ms |
So the lambda body cost 17.6 ms and building its inputs 9.6 ms, on tics where
the array being mapped is empty. Narrowing the clip to the question
P_ThingHeightClip actually asks, which drops the walk over things, the
pickup list and the special line list, bought 3 ms of the 17.6. The rest is
the line half: lines_in over bmap_lines, crossing per line and
opening per line.
Two approaches were measured and did not work.
- Gating the clip on
notEmpty gained 1 ms. ClickHouse evaluates both arms
of an if, so the captures are built either way.
- Making the pass's shared values ordinary bindings rather than one lambda
chain cost 353 ms per tic and took the analysis from 11.4 s to 28.8 s. A
value read five times in one SELECT is expanded five times, which is worse
than a chain that copies its arrays through its levels.
What it might look like
The thinkers are the large piece and want their own pass in the same shape
the plane thinkers ended up in: a cheap map over the list, one batch of
whatever heavy primitive the round needs, then a cheap map to apply it, with
a guard that leaves the tic unresolved wherever the shape cannot be honest.
For the cost, the untried idea is to hand the height clip a candidate line
list built once per tic from the moving sectors, so bmap_lines is not
reached from inside the lambda. The hazard is that
P_BlockLinesIterator stops at the first line that blocks and only the lines
reached before it move the floor and the ceiling, so the candidate list has
to arrive in the blockmap's own visit order or the heights change. That is
most of what lines_in already does, which is why it was not attempted.
Does this touch any of these?
The readers under native/tests/support/ work out the same answers in Rust.
They are test code and no statement reads them, so the driver still computes
nothing.
Would you want to implement it?
Yes
Where it lands
Decode or execute in SQL (sqlcpu)
What you are trying to do, and what stops you
Native mode runs
P_Tickerin SQL. The player walks, slides, reaches forlines, opens doors and drives plats, floors and switch timers. Two things
stop the parity run going further, and one of them is a cost.
The thinkers are not written.
demo3agrees with the reference emulatorfor every gametic from 2 to 205. At 206 the player's momentum parts, because
by then the engine has woken a monster, spawned what it fired and pushed the
player with it. What is missing is the round body over the thinker list:
P_MobjThinkerand the state cycle,A_LookandA_Chase, the attackfamily,
P_DamageMobjandP_KillMobj,P_SpawnMobjand the slow path.P_MovePspritesgoes with them: five fields are already open at gametic 2(
m_frame,m_tics,m_state,m_lastlook,psp_sy) and they are allwaiting on a thinker.
Until they land, nothing downstream can be checked against the trace.
demo3starts a plat at gametic 603 and a second door at 707, and neitheris reachable while the player is somewhere the engine did not put them. The
plats, floors and buttons that exist are tested against readers written from
p_plats.candp_spec.cwith the thinker seeded into a state row, whichis a real check but a weaker oracle than the emulator.
The activation path is only half there.
P_UseSpecialLinedispatches themanual door specials to
EV_VerticalDoor. The switch specials that reachEV_DoPlat,EV_DoFloor,EV_DoDoor,EV_DoCeiling,EV_BuildStairsandEV_DoDonutare not written, nor isP_ChangeSwitchTexture, norP_CrossSpecialLinefor the walkover triggers. A press that reaches one ofthose leaves the tic unresolved, which is honest but is not the engine.
The tic costs 54.7 ms against a budget of 20. That is with doors, plats,
floors and buttons in, measured over the 120 tics that include the door
moving, on an otherwise idle machine. It was 506 ms when the clip sat inside
the per-thinker fold.
Where the time goes, measured over gametics 2 to 61 where no plane moves and
the clip's input array is empty on every tic:
P_ThingHeightClipSo the lambda body cost 17.6 ms and building its inputs 9.6 ms, on tics where
the array being mapped is empty. Narrowing the clip to the question
P_ThingHeightClipactually asks, which drops the walk over things, thepickup list and the special line list, bought 3 ms of the 17.6. The rest is
the line half:
lines_inoverbmap_lines,crossingper line andopeningper line.Two approaches were measured and did not work.
notEmptygained 1 ms. ClickHouse evaluates both armsof an
if, so the captures are built either way.chain cost 353 ms per tic and took the analysis from 11.4 s to 28.8 s. A
value read five times in one SELECT is expanded five times, which is worse
than a chain that copies its arrays through its levels.
What it might look like
The thinkers are the large piece and want their own pass in the same shape
the plane thinkers ended up in: a cheap map over the list, one batch of
whatever heavy primitive the round needs, then a cheap map to apply it, with
a guard that leaves the tic unresolved wherever the shape cannot be honest.
For the cost, the untried idea is to hand the height clip a candidate line
list built once per tic from the moving sectors, so
bmap_linesis notreached from inside the lambda. The hazard is that
P_BlockLinesIteratorstops at the first line that blocks and only the linesreached before it move the floor and the ceiling, so the candidate list has
to arrive in the blockmap's own visit order or the heights change. That is
most of what
lines_inalready does, which is why it was not attempted.Does this touch any of these?
The readers under
native/tests/support/work out the same answers in Rust.They are test code and no statement reads them, so the driver still computes
nothing.
Would you want to implement it?
Yes