Announcing Specs 0.9, a parallel ECS library

We're happy to announce a new release of Specs, a highly parallel ECS.
It's been three months since the 0.8 version, which brought ticketed locks to you. Since then, there were many, many changes including

  • integration of shred (PR)
  • a new book to get you started with Specs (PR)
  • iterating over components in parallel (PR)
  • better documentation
  • a FlaggedStorage (PR)
  • a website for Specs, hosting the docs and the rendered book

Note: I'm leaving the old link here, but by now (09/2018) we've dropped the extra website and we now just use the book instead. So unless you want to admire the old (ugly) website, please visit

Additionally, we set up bors-ng, a GitHub application ensuring an evergreen master, similar to what @bors does in the Rust repos.

The new API utilizes "Aspects" to speed up system execution and get rid of all locks that were previously involved. Aspects are a way of telling Specs to which components a system needs read and to which write access. This is how it looks in code:

struct SysA;

impl<'a> System<'a> for SysA {
    type SystemData = (WriteStorage<'a, Pos>, ReadStorage<'a, Vel>);

    fn run(&mut self, data: Self::SystemData) {}

With that knowledge, Specs (or more acccurately, shred) can build up certain stages, to put it simply, a data structure which tells us

  1. in which order systems can be executed and
  2. which systems may run in parallel

After that, we make use of rayon's power to execute them in parallel.

This is an example of setting up these systems:

let mut d = DispatcherBuilder::new()
	.add(RequestSpawns, "req_spawns", &[])
	.add(GenCollisions, "gen_collisions", &[])
	.add(SpawnEnemies, "spawn", &["req_spawns"]) // we can specify dependencies between systems
	.add(Integrate, "integrate", &[])

You think executing the systems in parallel isn't enough? No, problem, let's do the same for the components:

impl<'a> System<'a> for UpdatePos {
    type SystemData = (WriteStorage<'a, Pos>, ReadStorage<'a, Vel>);

    fn run(&mut self, data: Self::SystemData) {
        use rayon::prelude::*;
        let (mut pos, vel) = data;
        (&mut pos, vel)
            .for_each(|(pos, vel)| pos.0 += vel.0);

Want to learn more about it? Then just start with the Hello World chapter. If you need any help, please join us on Gitter.


If you rename the github repo to have the literal name, then it will show up at the top-level instead of the subdomain.


What is an ECS?


An entity component system, apparently.

1 Like

This is some great polish! Your effort really shows. Thanks for working on this :slight_smile:

1 Like