Hi everyone,
I'm working on a complex simulation project using SimPy, where I'm modeling real-world processes in a digital twin for optimization purposes. For the optimization phase, I use a tree search strategy (similar to MCTS or other decision tree explorations), where each node represents a state of the system and a decision taken at that point.
The challenge I'm facing is related to simulation performance during deep tree searches. Each time I simulate a new node in the tree, I deterministically replay all prior actions and decisions from the root to that node. As the tree grows deeper, this becomes increasingly inefficient because SimPy has to replay a long sequence of events using env.run(until=event) repeatedly. Additionally, the simulation is already quite slow because at specific events I perform computationally heavy side-calculations (offloaded to another process or thread), and the simulation must pause until those are complete. I currently use env.run(until=external_event) to achieve this blocking behavior.
From my understanding, using yield in a process does not truly “pause” the simulation globally — it just pauses that process. But I need the entire simulation to halt until an external computation completes.
To improve performance, I'm now exploring the idea of snapshotting the simulation state (or environment) at certain points, so I can clone or fork from a prior state instead of re-running everything from the beginning.
I’m aware that directly copying a SimPy Environment (or Process) isn’t feasible because it relies on Python generators, which cannot be cloned or pickled. However, I’m wondering if there’s a clever workaround to:
I assume this involves serializing custom state (resources, variables, etc.), and ideally also capturing the event heap (or rebuilding it). However, I’m not sure if this is feasible or safe within SimPy’s architecture.
To illustrate the idea, here's a small example that mimics my current simulation structure:
import simpy
def worker(env, name, external_event):
print(f'{env.now}: {name} starts')
yield env.timeout(5)
print(f'{env.now}: {name} waiting for external event')
yield external_event
print(f'{env.now}: {name} resumes after external event')
def run_simulation(branch_decision):
env = simpy.Environment()
# Simulate some setup process
external_event = env.event()
env.process(worker(env, f"Worker-{branch_decision}", external_event))
# Simulate until the external computation point
env.run(until=6) # Simulation halts before external_event is triggered
# Here I would like to "snapshot" the environment to reuse this state later
# Instead of running everything from scratch every time
# External computation finishes
external_event.succeed()
env.run() # Finish simulation
if __name__ == "__main__":
for i in range(3): # Simulate different decisions
print(f"=== Branch {i} ===")
run_simulation(i)
In practice, the simulation and external computations are much more complex and the tree search may involve thousands of branches, where each one repeats the same deterministic prefix. So if I could “fork” the simulation after some common prefix (say time t=6 above), I would save massive amounts of computation.
My Questions:
Any guidance, workarounds, or thoughts are greatly appreciated!
Best regards,
Henrik
--
You received this message because you are subscribed to the Google Groups "python-simpy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-simpy...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/python-simpy/33dd0d14-a2a9-42bf-a8c0-8c756f71905cn%40googlegroups.com.