-
Notifications
You must be signed in to change notification settings - Fork 9
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
Ensuring determinism with periodics #34
Comments
A subtle topic indeed! TL;DR: it is not possible to ensure determinism with In general, the scheduler attempts to strike a balance between efficiency and determinism. The minimum guarantee when it comes to determinism is that the execution order must be preserved within messages addressed to the same model. To achieve this, when events are polled from the scheduler queue (which preserves scheduling order), all events targeting the same model are fused into a single sequential In the development branch, You can take advantage of that by scheduling two periodic The scope for version 0.3 keeps on expanding so I certainly won't consider this for the next version, but some simulators have a concept of super-dense time where the timestamp has an additional integer part that make it possible to decompose a time step into several sub-steps. AFAIK this concept does not exist in the |
This issue prompted further internal discussions and we came to the conclusion that the current ordering guaranties for scheduling are not fully consistent with the ordering guaranties for non-scheduled messages (those sent from What we should have done in retrospect is to guaranty ordered sequential execution for same-time events based on the model that scheduled them rather than based on the models targetted by such events. Doing this would provide exactly the same kind of determinism as for regular messages: if model In this context it makes sense to treat the main scheduler as a model, which would give exactly the determinism you expected, whether an event or an action is used. This is thus added to the |
That makes sense -- thanks so much! I don't need it urgently but definitely a desirable feature in the next 3 months or so for me. |
I had a follow-up question: after the changes you described are implemented, under what conditions will an Asynchronix simulation be fully deterministic? Will it be sufficient for all constituent models to run deterministically and possess no shared state? |
Short answer: the only way to ensure full determinism is to run the simulation on a single thread ( Full determinism can be important for debugging, but otherwise if the good behavior of a simulation requires more guaranties than we provide I would be a bit worried that there isn't enough isolation in the specific implementation of the models, resulting in a fragile setup. For instance if model If you are mostly worried about debugging though, then apart form enforcing determinism with single-threaded execution it will be possible to take advantage of tracing support in the |
Ah, I'm mostly interested in determinism for debugging and reproducibility (e.g. wanting to be able to cache test results in CI sim runs), not to ensure correctness -- agree it would seem quite fragile to rely on particular e.g. execution orderings for correctness. Hmm, say I have a simulation with models |
I don't see anything that could cause a non-deterministic outcome in your example, which makes me worry that I may have in turn misunderstood the situation you are describing... Just to be sure: But in broad terms, a non-deterministic outcome with the parallel executor would be rare and usually symptomatic of a flawed bench design; I just meant to say that it is theoretically possible. |
Right, they don't communicate. I was trying to provide a parallel example that is nonetheless guaranteed to be deterministic -- doesn't this run contrary to what you said earlier?
Maybe by "full" you mean "across all possible simulation benches"? What I'm interested in is what characteristics a particular simulation bench must possess in order to ensure that it will always execute deterministically regardless of parallelism. For me it seems useful to publish a sufficient condition for this, as general as possible, since determinism is often a desirable property, and since it is not in general guaranteed (if I understand correctly). I guess in other words I'm looking for a precise definition of what you mean by "flawed". For example, although not very general, would this be correct?
In my use case, I have been planning to construct a limited, tailored wrapper around Asynchronix that strongly encourages simulations to be deterministic (among other things), so publishing guidance like this would be helpful in informing my design. |
Yes, this is what I meant, sorry if this was confusing. In general, even without performing I/O or using shared state, it is always theoretically possible for some bench topologies to get a different outcome with parallel execution because the operating system might randomly preempt the execution of worker threads. Since the executor uses a work-stealing strategy and each worker thread picks any task that is ready to be executed, this can result in different orders of execution. A very useful thought experiment to determine if there may be non-determinism is to consider that the executor is in fact fully deterministic (!) and message queues are ordered and latency-free, but that the time it takes for models to process an event is unknown. If this unknown time plays a role in the outcome, then this is a source of non-determinism. So the fundamental root of non-determinism is in the bench topology, and things like I/O are simply other elements beside preemption that can trigger the latent non-determinism. Regarding your point 3) above, note that there is no difference in this respect between Example of deterministic scenario: Example of non-deterministic scenario: In my experience working with simulators from large space integrators, even though they were technically single-threaded and thus deterministic, we would anyway need to make sure that the above rules were upheld because the order in which models would be scheduled was an implementation detail on which we couldn't rely (interestingly, even events sent sequentially within one function were not necessarily scheduled in that sequential order...). |
I'm wondering about determinism in the following simplified situation: Say we have two models,
A
andB
, each with anexecute
method that we schedule to run periodically at the same periodicity and no phase offset. Thus for each cycle,A::execute
andB::execute
will run at the same simulated time. Say further that theexecute
method forA
immediately sends an event toB
, which modifiesB
's state, affecting whatB
'sexecute
method will do. As a result, without a way of specifying which periodic executes first each cycle, there appears to be a race condition possible where the outcome of the simulation isn't fully determined/predictable.Am I understanding this right? Are there ordering guarantees for periodics? Is there a way to ensure determinism, other than something hacky like adding tiny phase offsets? Would appreciate any context that can be provided. Thanks so much!
The text was updated successfully, but these errors were encountered: