并行查询: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| { ... }) 对每个匹配的实体执行闭包。闭包会在多个线程上并发调用。
注意事项
- 闭包内不能访问
Res、ResMut等系统参数——它们不在并行迭代的上下文中。需要的数据要提前提取到局部变量。 - 闭包的参数类型是查询的只读/可变引用组合(如
(&mut Position, &Velocity))。 - 只有当实体数量足够多时,并行才有意义。少量实体的并行开销(线程调度、同步)可能大于收益。
par_iter()(不可变版本)用于只读查询。
性能调优
ComputeTaskPool 的线程数默认等于 CPU 核心数。如果需要调整,可以在 TaskPoolPlugin 中配置。但通常默认值已经是最优的——线程数等于核心数能最大化吞吐量。
对于少量实体但每个实体计算量大的场景,更好的做法是把计算逻辑整体提交给 AsyncComputeTaskPool,而不是用 par_iter_mut。