-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
react immediately to wakeups #1736
Conversation
7f0bd39
to
670f316
Compare
splitLookup
for wakeups
670f316
to
c8fd3f5
Compare
13a8081
to
fcd46f0
Compare
fcd46f0
to
8920058
Compare
256eca0
to
bd968b9
Compare
bf9aaa7
to
ad5c589
Compare
Currently looking through this. How does it interact with the single-step debugging mode? Do we need to update the |
Yes, I suppose we would. Will look into this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This all looks good to me, with the possible caveat of needing to update singleStep
.
7f0b299
to
80d5b76
Compare
This improves the test introduced in #1736 in a few ways: * Place the system robot's code inline in the `program` field * even though the system bot had been using `instant`, the `run` command itself incurs a delay. So this ensures the system robot is ready to monitor the cell immediately * System robot uses exclusively `Intangible` commands in its monitoring loop * This allows removal of the `instant` command. * `Surveil`, which does not appear in any scenario yet, is made `Intangible`. * Remove the initial `wait 2` in the player robot's solution. Tested to ensure that this still exercises the problem that #1736 set out to solve, by commenting out this line (causing the scenario to fail). https://github.com/swarm-game/swarm/blob/01ae0e45d75c30619af76ae2438dc1b92ef61e08/src/swarm-engine/Swarm/Game/State/Robot.hs#L396 ## Demo scripts/play.sh -i data/scenarios/Testing/1598-detect-entity-change.yaml --autoplay --speed 1
Fixes #1598
Demo
Illustrates immediate (same-tick) reaction to a change of a
watch
ed cell:Background
Robots can be scheduled to wakeup from a
watch
by thewakeWatchingRobots
function.Robots may be actually awakened by the
wakeUpRobotsDoneSleeping
function.Previously,
wakeWatchingRobots
would only ever schedule wakeups atcurrentTick + 1
. Then, once the next tick is reached, inwakeUpRobotsDoneSleeping
only robots that had been scheduled to wake on precisely the current tick could actually be awakened.But this made it impossible for one robot to cause another robot, which had been sleeping, to actually wake up within the same tick that scheduling of the wakeup is performed.
The importance of this comes into play for system robots that are
watch
ing a cell and intended to react instantaneously to player actions (usually by running code in aninstant
block).During each "tick", every active robot gets exactly one "turn" to execute code. Given the following ID assignments:
0
base
1
systemBot
the
systemBot
will always execute after thebase
robot in a given tick.If the
systemBot
iswatch
ing a given cell, a modification of that cell by thebase
will schedule a wakeup of thesystemBot
. If the scheduled wakeup tick iscurrentTick + 1
, then thebase
will have another execution turn before thesystemBot
gets an opportunity to react to thebase
's action at the current tick, causing thesystemBot
to overlook actions that may be relevant to a goal condition.Solution
In contast, if we schedule the
systemBot
wakeup forcurrentTick
instead ofcurrentTick + 1
, then it should have an opportunity to wake, run, and react to thebase
's action before thebase
's next turn.But in the status quo, the selection of which robots to run in a given tick is made only once, before iteration over those robots begins. We need instead to update the robots-to-run pool dynamically, after each iteration on an individual robot. The most appropriate data structure for the iteration pool is probably a Monotone priority queue, but we approximate this with an
IntSet
ofRID
s and theminView
function.Being able to alter the list of active robots in the middle of a given tick's robot iteration has required a change to the
runRobotIDs
function. Instead of using aforM_
to iterate over a static list ofRIDs
, we have a custom iteration functioniterateRobots
to handle the dynamic set.Performance
Benchmarks were performed locally with
stack
and GHC 9.6.I had tested the replacement of the
forM_
loop withiterateRobots
in isolation, and it had zero effect on benchmarks. But some other trivial changes tested in isolation, such as the addition of docstrings, yielded a +6% increase in benchmarks. So there seems to be some instability in the ability of the compiler to perform certain optimizations.With this PR, increases in benchmark times ranging from 7% to 11% were observed. The obvious culprit is that
wakeUpRobotsDoneSleeping
is now called N times per tick, rather than once per tick, where N is the number of robots that are initially (or become) active during that tick.Mitigating with an "awakened" set
In the common case (i.e. when there are no watching robots), the
wakeUpRobotsDoneSleeping
that is now executedN
times per tick (instead of once per tick) incurs aMap
lookup, which isO(log M)
in the number of distinct future wakeup times across all robots.However, those extra
N
invocations only exist to serve the purpose ofwatch
ing robots that may need to wake up at the current tick. It may be more efficient to have a dedicatedSet
in theGameState
for such robots that gets populated parallel to this insertion:swarm/src/swarm-engine/Swarm/Game/State/Robot.hs
Line 387 in ad5c589
Then if this set remains
null
, we can avoid paying for an additionalO(N * log M)
operations entailed by theMap
lookups intointernalWaitingRobots
.Result
Indeed, storing awakened bots in a new
currentTickWakeableBots
set restored the benchmarks nearly to the baseline. Instead of the regressions in the 10-20% range observed before, now only a few benchmarks had at most 3-4% increases.CI regressions
In CI, (
snake
,powerset
, andword-search
) exceeded their timeout thresholds in GHC 9.2 and GHC 9.4. No regressions were observed with GHC 9.6. To accommodate the lowest common denominator, I bumped the thresholds for those three scenarios to get a passing build.I don't believe it is worth further effort or investigation to optimize for GHC 9.4, since such efforts will be moot for GHC 9.6 and onward.
Test invocation:
snake
powerset
word-search
Potential improvements
TickNumber
asMap
keywaitingRobots
is of typeMap TickNumber [RID]
. Instead ofMap
, we could have anewtype
that encapsulates aIntMap
to make lookups byTickNumber
more efficient.