Skip to content

Faster scope completion #7

@chescock

Description

@chescock

What problem does this solve or what need does it fill?

When a ThreadPool::scope call runs out of jobs from the current scope before the scope is complete, it runs other jobs from the pool. That works well if the jobs are small and uniform, but for Bevy this can cause Query::par_iter() calls to start executing other systems while waiting! If the stolen systems are long and the blocked system has a lot of dependencies, that removes a lot of concurrency from the schedule.

What solution would you like?

Have Scope::complete only run jobs from the current scope. If it runs out of local jobs before the scope is complete, put the thread to sleep instead of running other jobs, so that it can be awakened immediately when the scope is completed.

Then, to make sure the CPUs are all utilized, add a new worker thread to the pool. When the scope is finally complete, remove the thread that completed it to compensate. This will usually be a different thread then the one added.

It may be possible to do this without resizing the pool by sending the WorkerThread value to the new thread.

Additional context

For comparison, here are the approaches taken by some other pools:

  • Rayon runs other work. It assumes that jobs are small, so there won't be much delay in waiting.
  • async_scoped can be used with Tokio and does something similar to this proposal. It calls tokio::task::block_in_place, which sends the local queue to a new thread. But if another thread does take the queue, the original one never takes it back! That means it runs one too many threads until the original job completes. It assumes the original job was small and will complete quickly once the block_in_place call is done.
  • bevy_tasks either runs other work or blocks, based on the tick_task_pool_executor parameter.
    • The multi-threaded executor passes false, blocking the thread. This runs on the main thread, and we want to keep it idle to handle non-send and exclusive systems.
    • Everything else (including Query::par_iter()) passes true, running other work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions