Using parallelism properly for a game

Hello,

I'm coding a game which have a lot of computing to do. I want to compute some behaviors x times each seconds. I think to use rayon to parallelize the computing. Design would be :

use rayon::prelude::*;

pub struct Entity {
    id: i64,
}

#[derive(Debug)]
pub enum Message {
    DoSomething(usize, i64),
}

pub struct Engine {
    entities: Vec<Entity>,
}

impl Engine {
    pub fn tick(&mut self) {
        // Parallelize by executing tick_entity method on every cpu core
        let results: Vec<Vec<Message>> = (0..self.entities.len())
            .into_par_iter()
            // Indicate to tick_entity which entity to compute
            .map(|i| self.tick_entity(i))
            .collect();

        // In main thread, apply results which require mutability
        for messages in results {
            self.react(messages);
        }
    }

    fn tick_entity(&self, i: usize) -> Vec<Message> {
        vec![Message::DoSomething(i, self.entities[i].id)]
    }

    fn react(&mut self, _messages: Vec<Message>) {
        // React according to messages
    }
}

fn main() {
    // Simulate 100 entities
    let entities = (0..100).map(|i| Entity { id: i }).collect();
    let mut engine = Engine { entities };

    // Simulate 60 ticks (like 60 fps)
    for _ in 0..60 {
        engine.tick();
    }
}

So, I think to parallelize computing on entities (as non-mutable) and produce all future behaviors which require mutability (Message). The behaviors will be applied with mutability in main thread (non-parallelized).

Is that a good design ? Rayon thread creation (many times per seconds) have a reduced cost ?

Thanks !

Yeah, this seems like a good design :+1: Exactly the kind of thing rayon is meant for!

Only two things I'd suggest changing:

1: Use flat_map instead to prevent storing several Vecs:

let results: Vec<Message> = (0..self.entities.len())
            .into_par_iter()
            .flat_map(|i| self.tick_entity(i))
            .collect();

self.react(results);

2: Use self.entities.par_iter() instead of (0..self.entities.len()).par_iter(), and give tick_entity a reference to each entity rather than just the ID.


Rayon uses a pre-existing thread pool to run in parallel, it doesn't create new threads every time, so no need to worry about that. :slight_smile:

Wonderful ! Thank you !

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.