docs/bevy/release-content/release-notes/schedule_randomization.md 2.6 K · 50 lines · raw · history

1 ---
2 title: Schedule randomization
3 authors: ["@andriyDev"]
4 pull_requests: [25094]
5 ---
6
7 Before a schedule runs (and therefore, your systems), it first computes the order that systems run
8 in based on the ordering constraints (`.before()`, `.after()`, `.chain()`) of systems and system
9 sets. However, in addition to this, the schedule must also resolve **conflicts** - if system A and
10 system B both mutate component C, and there's no ordering between A and B, the schedule needs to
11 pick one to run first. So far, the rule has been that this is non-deterministic.
12
13 In practice though, schedules pick the order of these conflicting systems "deterministically, but
14 arbitrarily". Put simply, your systems might accidentally be in the right order, but making an
15 unrelated change to the graph might suddenly put it in the wrong order. This problem can be very
16 difficult to detect.
17
18 Introducing schedule randomization! This will randomize the order of systems while maintaining any
19 explicit system ordering constraints. Once the `debug` feature is enabled, `ScheduleBuildSettings`
20 will include a `shuffle_seed` field, that users can set to randomize their schedules. For example:
21
22 ```rust
23 App::new()
24 .add_plugins(DefaultPlugins)
25 .edit_schedule(Update, |schedule| {
26 // Make sure to add the `rand` crate with `cargo add rand`.
27 let rng_seed: u64 = rand::random();
28 // Consider logging out the seed, so you can reproduce the error if you find a bug!
29 info!("Randomizing Update schedule with seed={rng_seed}");
30 schedule.set_build_settings(ScheduleBuildSettings {
31 shuffle_seed: Some(rng_seed),
32 ..Default::default()
33 });
34 })
35 .run();
36 ```
37
38 This can be used for "property testing", to verify that your systems satisfy some property despite
39 different orderings of systems.
40
41 There are some caveats however. Currently, when using `auto_insert_apply_deferred`, systems with
42 commands are always placed before the earliest sync point they can. This means that although your
43 systems may not have the correct ordering, they might "accidentally" have the correct ordering
44 because of which sync point it uses. We hope to fix this in the future.
45
46 In addition, the multi-threaded executor executes systems greedily: it looks for the first
47 unexecuted system whose dependencies are finished and that has no other conflicting systems running.
48 The result is that even if the shuffle results in the order `(A, B, C)`, `C` could run before `B` if
49 `A` and `B` conflict. **This can be desirable to test**, but consider using the single-threaded
50 executor to avoid this case.