8 个版本 (重大变更)

0.7.0 2023年11月8日
0.6.0 2023年5月30日
0.5.0 2022年8月15日
0.4.2 2022年6月7日
0.1.0 2022年4月8日

#123并发

Download history 48/week @ 2024-04-07 22/week @ 2024-04-14 29/week @ 2024-04-21 7/week @ 2024-04-28 21/week @ 2024-05-05 8/week @ 2024-05-12 14/week @ 2024-05-19 57/week @ 2024-05-26 52/week @ 2024-06-02 63/week @ 2024-06-09 14/week @ 2024-06-16 12/week @ 2024-06-23 2/week @ 2024-06-30 62/week @ 2024-07-07 64/week @ 2024-07-14 39/week @ 2024-07-21

167 每月下载量
4 个 Crates 中使用 (3 个直接使用)

MIT 许可

37KB
760

合唱团

Crates.io Docs.rs Build Status MSRV codecov.io

合唱团是一个任务编排框架。它帮助您根据任务来组织所有 CPU 工作流程。

示例

let mut choir = choir::Choir::new();
let _worker = choir.add_worker("worker");
let task1 = choir.spawn("foo").init_dummy().run();
let mut task2 = choir.spawn("bar").init(|_| { println!("bar"); });
task2.depend_on(&task1);
task2.run().join();

销售要点

是什么让合唱团如此优雅?通常当我们需要编码“等待依赖”的语义时,我们会想到某种计数器。可能是一个原子的,用于依赖项数量。当它达到零(或一)时,我们安排任务执行。在 合唱团 中,任务的内部数据(即自身运算符!)被放置在一个 Arc 中。每次我们能够从 Arc 中提取它(这意味着没有其他依赖项),我们就将它移动到调度队列中。我认为 Rust 类型系统在这里表现最好。

注意:实际上 Arc 并不完全支持这里所需的此类“线性”使用,而且无法控制最后一个引用在何处被销毁(没有 drop() 中的逻辑)。因此,我们引入了自己的 Linearc 来内部使用。

您还可以随时添加或删除工作器,以平衡系统负载,这可能同时在运行其他应用程序。

API

一般工作流程是创建任务并在它们之间设置依赖关系。有多种不同类型的任务

  • 单次运行任务,使用 init() 初始化,表示为 FnOnce()
  • 空任务,使用 init_dummy() 初始化,没有函数体
  • 多次运行任务,在范围中的每个索引上执行,表示为 Fn(SubIndex),并使用 init_multi() 初始化
  • 迭代任务,为迭代器产生的每个项目执行,表示为 Fn(T),并使用 init_iter() 初始化。

如果没有显式调用,则在 IdleTask::drop() 上自动调用 run()。此对象还允许在调度任务之前添加依赖项。运行中的任务也可以用作其他任务的依赖项。

请注意,所有任务都在 Fn() 执行边界处被抢占。因此,例如,一个长时间运行的多任务将被任何传入的单次运行任务抢占。

用户

Blade 在并行化资源加载方面严重依赖于 Choir。有关详细信息,请参阅 Rust Gamedev Meetup 上的 blade-asset talk

TODO

开销

机器:2016年MBP,3.3 GHz 双核英特尔酷睿i7

  • 函数 spawn()+init()(优化):237纳秒
  • "窃取"任务:61纳秒
  • 空"执行":37纳秒
  • 空任务"解除阻塞":78纳秒

执行100k个空任务

  • 单独执行:28毫秒
  • 作为多任务:6毫秒

性能分析工作流程示例

使用 Tracy:将此行添加到基准测试的开头

let _ = profiling::tracy_client::Client::start();

然后在命令提示符下运行

cargo bench --features "profiling/profile-with-tracy"

依赖项

~440KB