specs 0.16.1

Specs is an Entity-Component-System library written in Rust.
Documentation
#![feature(test)]

#[macro_use]
extern crate criterion;
extern crate rand;
extern crate rayon;
extern crate specs;
extern crate test;

use criterion::{Bencher, Criterion};
use specs::{prelude::*, storage::HashMapStorage};

#[derive(Clone, Debug)]
struct CompInt(i32);

impl Component for CompInt {
    type Storage = VecStorage<Self>;
}

#[derive(Clone, Debug)]
struct CompBool(bool);

impl Component for CompBool {
    type Storage = HashMapStorage<Self>;
}

fn create_world() -> World {
    let mut w = World::new();

    w.register::<CompInt>();
    w.register::<CompBool>();

    w
}

fn world_build(b: &mut Bencher) {
    b.iter(World::new);
}

fn create_now(b: &mut Bencher) {
    b.iter_with_large_setup(
        || World::new(),
        |mut w| {
            w.create_entity().build();
        },
    );
}

fn create_now_with_storage(b: &mut Bencher) {
    b.iter_with_large_setup(
        || create_world(),
        |mut w| {
            w.create_entity().with(CompInt(0)).build();
        },
    );
}

fn create_pure(b: &mut Bencher) {
    b.iter_with_large_setup(
        || World::new(),
        |w| {
            w.entities().create();
        },
    );
}

fn delete_now(b: &mut Bencher) {
    b.iter_with_setup(
        || {
            let mut w = create_world();
            let eids: Vec<_> = (0..100).map(|_| w.create_entity().build()).collect();

            (w, eids)
        },
        |(mut w, mut eids)| {
            if let Some(id) = eids.pop() {
                w.delete_entity(id).unwrap()
            }
        },
    );
}

fn delete_now_with_storage(b: &mut Bencher) {
    b.iter_with_setup(
        || {
            let mut w = create_world();
            let eids: Vec<_> = (0..100)
                .map(|_| w.create_entity().with(CompInt(1)).build())
                .collect();

            (w, eids)
        },
        |(mut w, mut eids)| {
            if let Some(id) = eids.pop() {
                w.delete_entity(id).unwrap()
            }
        },
    );
}

fn delete_later(b: &mut Bencher) {
    let mut w = World::new();
    let mut eids: Vec<_> = (0..10_000_000).map(|_| w.create_entity().build()).collect();
    b.iter(|| {
        if let Some(id) = eids.pop() {
            w.entities().delete(id).unwrap()
        }
    });
}

fn create_after_delete(b: &mut Bencher) {
    use rand::prelude::*;

    let mut rng = thread_rng();
    b.iter_with_setup(
        || {
            let mut w = World::new();
            let eids: Vec<_> = (0..1000).map(|_| w.create_entity().build()).collect();

            (0..1000)
                .choose_multiple(&mut rng, 100)
                .into_iter()
                .map(|i| eids[i])
                .for_each(|e| {
                    w.delete_entity(e)
                        .expect("Failed deleting entity in 'create after delete' setup");
                });

            w.maintain();

            w
        },
        |mut w| {
            for _ in 0..100 {
                w.create_entity().build();
            }
        },
    )
}

fn create_after_delete_extreme(b: &mut Bencher) {
    b.iter_with_setup(
        || {
            let mut w = World::new();

            let eids: Vec<_> = (0..1000).map(|_| w.create_entity().build()).collect();

            w.delete_entity(eids[0])
                .expect("Failed deleting first entity in 'create after delete extreme' setup");
            w.delete_entity(eids[999])
                .expect("Failed deleting last entity in 'create after delete extreme' setup");
            w.maintain();

            w
        },
        |mut w| {
            w.create_entity().build();
            w.create_entity().build();
        },
    )
}

fn maintain_noop(b: &mut Bencher) {
    let mut w = World::new();
    b.iter(|| {
        w.maintain();
    });
}

fn maintain_add_later(b: &mut Bencher) {
    let mut w = World::new();
    b.iter(|| {
        w.entities().create();
        w.maintain();
    });
}

fn maintain_delete_later(b: &mut Bencher) {
    let mut w = World::new();
    let mut eids: Vec<_> = (0..10_000_000).map(|_| w.create_entity().build()).collect();
    b.iter(|| {
        if let Some(id) = eids.pop() {
            w.entities().delete(id).unwrap();
        }
        w.maintain();
    });
}

fn join_single_threaded(b: &mut Bencher) {
    use test::black_box;

    let mut world = World::new();
    world.register::<CompInt>();

    {
        let entities: Vec<_> = world.create_iter().take(50_000).collect();
        let mut comp_int = world.write_storage();
        for (i, e) in entities.iter().enumerate() {
            comp_int.insert(*e, CompInt(i as i32)).unwrap();
        }
    }

    b.iter(|| {
        for comp in world.read_storage::<CompInt>().join() {
            black_box(comp.0 * comp.0);
        }
    })
}

fn join_multi_threaded(b: &mut Bencher) {
    use rayon::prelude::*;
    use test::black_box;

    let mut world = World::new();
    world.register::<CompInt>();

    {
        let entities: Vec<_> = world.create_iter().take(50_000).collect();
        let mut comp_int = world.write_storage();
        for (i, e) in entities.iter().enumerate() {
            comp_int.insert(*e, CompInt(i as i32)).unwrap();
        }
    }

    b.iter(|| {
        world.read_storage::<CompInt>().par_join().for_each(|comp| {
            black_box(comp.0 * comp.0);
        })
    })
}

fn world_benchmarks(c: &mut Criterion) {
    c.bench_function("world build", world_build)
        .bench_function("create now", create_now)
        .bench_function("create pure", create_pure)
        .bench_function("create now with storage", create_now_with_storage)
        .bench_function("delete now", delete_now)
        .bench_function("delete now with storage", delete_now_with_storage)
        .bench_function("delete later", delete_later)
        .bench_function("create after delete", create_after_delete)
        .bench_function("create after delete extreme", create_after_delete_extreme)
        .bench_function("maintain noop", maintain_noop)
        .bench_function("maintain add later", maintain_add_later)
        .bench_function("maintain delete later", maintain_delete_later)
        .bench_function("join single threaded", join_single_threaded)
        .bench_function("join multi threaded", join_multi_threaded);
}

criterion_group!(world, world_benchmarks);

criterion_main!(world);