Skip to content

并行查询:par_iter_mut

当查询结果数量很大(成千上万个实体),且每个实体的处理相互独立时,Query::par_iter_mut() 可以把工作分发到 ComputeTaskPool 的多个线程上并行执行。

rust
use bevy::prelude::*;

#[derive(Component)]
struct Position(Vec3);

#[derive(Component)]
struct Velocity(Vec3);

fn main() {
    App::new()
        .add_plugins(MinimalPlugins)
        .add_systems(Startup, spawn_particles)
        .add_systems(Update, move_particles)
        .run();
}

fn spawn_particles(mut commands: Commands) {
    for i in 0..10_000 {
        commands.spawn((
            Position(Vec3::new(i as f32 * 0.01, 0.0, 0.0)),
            Velocity(Vec3::new(0.0, (i as f32 * 0.1).sin(), 0.0)),
        ));
    }
    info!("已生成 10,000 个粒子");
}

fn move_particles(mut query: Query<(&mut Position, &Velocity)>, time: Res<Time>) {
    query.par_iter_mut().for_each(|(mut pos, vel)| {
        pos.0 += vel.0 * time.delta_secs();
    });
}

Listing 34-4:par_iter_mut——并行更新 10,000 个粒子

par_iter_mut() 返回 QueryParIter,调用 .for_each(|item| { ... }) 对每个匹配的实体执行闭包。闭包会在多个线程上并发调用。

注意事项

  • 闭包内不能访问 ResResMut 等系统参数——它们不在并行迭代的上下文中。需要的数据要提前提取到局部变量。
  • 闭包的参数类型是查询的只读/可变引用组合(如 (&mut Position, &Velocity))。
  • 只有当实体数量足够多时,并行才有意义。少量实体的并行开销(线程调度、同步)可能大于收益。
  • par_iter()(不可变版本)用于只读查询。

性能调优

ComputeTaskPool 的线程数默认等于 CPU 核心数。如果需要调整,可以在 TaskPoolPlugin 中配置。但通常默认值已经是最优的——线程数等于核心数能最大化吞吐量。

对于少量实体但每个实体计算量大的场景,更好的做法是把计算逻辑整体提交给 AsyncComputeTaskPool,而不是用 par_iter_mut