cecs 0.1.11

Entity database for the game 'Cao-Lo'
Documentation
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use std::hint::black_box;

use cecs::prelude::*;

macro_rules! components {
    ($ ($x: ident),*) => {

        $(
        #[derive(Clone)]
        #[allow(unused)]
        struct $x (pub [u8; 32]);
        )*

        fn add_entity<'a>(cmd: &'a mut Commands) -> &'a mut EntityCommands {
            let mut cmd = cmd.spawn();
            $(
                if fastrand::bool() {
                    cmd = cmd.insert(
                        $x([42; 32])
                    );
                }
            )*
            cmd
        }

        fn make_stage() -> SystemStage<'static> {
            SystemStage::new("test")
            $(
                .with_system(move |_q: Query<&mut $x>| {})
            )*
            .build()
        }

    };
}

components!(
    C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17, C18, C19, C20, C21,
    C22, C23, C24, C25, C26, C27, C28, C29, C30, C31, C32, C33, C34, C35, C36, C37, C38, C39, C40,
    C41, C42, C43, C44, C45, C46, C47, C48, C49, C50
);

#[derive(Clone, Copy)]
struct TestComponent(pub u64);

fn benchmark_queries(c: &mut Criterion) {
    fastrand::seed(0xdeadbeef);
    let mut group = c.benchmark_group("query");

    for n in (10..=14).step_by(2) {
        let n = 1 << n;
        let mut world = World::new(n);

        world
            .run_system(|mut cmd: Commands| {
                for _ in 0..n {
                    add_entity(&mut cmd).insert(TestComponent(0));
                }
            })
            .unwrap();

        group.bench_with_input(BenchmarkId::new("mutate-single-serial", n), &n, |b, _n| {
            b.iter(|| {
                world
                    .run_system(|mut q: Query<&mut TestComponent>| {
                        for c in q.iter_mut() {
                            c.0 = 0xBEEF;
                        }
                    })
                    .unwrap();

                black_box(&world);
            });
        });

        group.bench_with_input(
            BenchmarkId::new("mutate-single-parallel", n),
            &n,
            |b, _n| {
                b.iter(|| {
                    world
                        .run_system(|mut q: Query<&mut TestComponent>| {
                            q.par_for_each_mut(|c| {
                                c.0 = 0xBEEF;
                            });
                        })
                        .unwrap();

                    black_box(&world);
                });
            },
        );
    }
}

fn benchmark_systems(c: &mut Criterion) {
    let mut group = c.benchmark_group("system_stages");

    group.bench_function("tick", |b| {
        let mut world = World::new(16);
        world.add_stage(make_stage());
        b.iter(|| {
            world.tick();
        });
    });

    group.bench_function("reuse-stage", |b| {
        let mut world = World::new(16);
        let mut stage = make_stage();
        b.iter(move || {
            stage = world.run_stage(std::mem::take(&mut stage)).unwrap();
        });
    });

    group.bench_function("clone-stage", |b| {
        let mut world = World::new(16);
        let stage = make_stage();
        b.iter(move || {
            world.run_stage(stage.clone()).unwrap();
        });
    });
}

#[cfg(feature = "clone")]
fn benchmark_commands(c: &mut Criterion) {
    let mut group = c.benchmark_group("commands");

    group.bench_function("spawn-10k-entity-inserts-components", |b| {
        b.iter_batched(
            || World::new(10000),
            |mut world| {
                world
                    .run_system(|mut cmd: Commands| {
                        for _ in 0..10000 {
                            cmd.spawn().insert(C1);
                            cmd.spawn().insert(C2);
                            cmd.spawn().insert(C3);
                            cmd.spawn().insert(C4);
                            cmd.spawn().insert(C5);
                            cmd.spawn().insert(C6);
                        }
                    })
                    .unwrap();
            },
            criterion::BatchSize::LargeInput,
        );
    });

    group.bench_function("spawn-10k-entity-bundle", |b| {
        b.iter_batched(
            || World::new(10000),
            |mut world| {
                world
                    .run_system(|mut cmd: Commands| {
                        for _ in 0..10000 {
                            cmd.spawn().insert_bundle((C1, C2, C3, C4, C5, C6));
                        }
                    })
                    .unwrap();
                world
            },
            criterion::BatchSize::LargeInput,
        );
    });

    group.bench_function("add-component-to-10k-existing-entity", |b| {
        let mut world = World::new(10000);

        world
            .run_system(|mut cmd: Commands| {
                for _ in 0..10000 {
                    cmd.spawn().insert_bundle((C1, C2, C3, C4, C5, C6));
                }
            })
            .unwrap();

        b.iter_batched(
            || world.clone(),
            |mut world| {
                world
                    .run_system(|mut cmd: Commands, q: Query<EntityId>| {
                        for id in q.iter() {
                            cmd.entity(id).insert_bundle((C7, C8, C9, C10, C11, C12));
                        }
                    })
                    .unwrap();
            },
            criterion::BatchSize::PerIteration,
        );
    });

    group.bench_function("remove-component-from-10k-existing-entity", |b| {
        let mut world = World::new(10000);

        world
            .run_system(|mut cmd: Commands| {
                for _ in 0..10000 {
                    cmd.spawn()
                        .insert_bundle((C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12));
                }
            })
            .unwrap();

        b.iter_batched(
            || world.clone(),
            |mut world| {
                world
                    .run_system(|mut cmd: Commands, q: Query<EntityId>| {
                        for id in q.iter() {
                            cmd.entity(id).remove::<C7>();
                        }
                    })
                    .unwrap();
            },
            criterion::BatchSize::PerIteration,
        );
    });

    group.bench_function("delete-10k-entity", |b| {
        let mut world = World::new(10000);

        world
            .run_system(|mut cmd: Commands| {
                for _ in 0..10000 {
                    cmd.spawn()
                        .insert_bundle((C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12));
                }
            })
            .unwrap();

        b.iter_batched(
            || world.clone(),
            |mut world| {
                world
                    .run_system(|mut cmd: Commands, q: Query<EntityId>| {
                        for id in q.iter() {
                            cmd.delete(id);
                        }
                    })
                    .unwrap();
            },
            criterion::BatchSize::PerIteration,
        );
    });
}

criterion_group!(query_benches, benchmark_queries);
criterion_group!(systems_benches, benchmark_systems);
criterion_group!(command_benches, benchmark_commands);
criterion_main!(query_benches, systems_benches, command_benches);