4个版本
0.2.2 | 2024年4月4日 |
---|---|
0.2.1 | 2024年3月6日 |
0.2.0 | 2023年8月15日 |
0.1.0 | 2023年1月16日 |
#8 in 模拟
71 每月下载量
420KB
7K SLoC
Asynchronix
Asynchronix 是一个开发者友好的、高度优化的离散事件模拟框架,用 Rust 编写。它旨在从小型简单模拟扩展到具有复杂时间驱动状态机的大型模拟平台。
概述
Asynchronix 是一个利用异步编程通过自定义多线程执行器透明高效地并行化模拟的模拟器。
它倡导一个系统工程师熟悉的面向组件的架构,类似于基于流的编程:模型本质上是一个具有固定类型输入和输出的孤立实体,通过在平台组装期间定义的连接通过消息传递与其他模型通信。
尽管其发展的主要动力是处理大型物理信息系统的模拟器的需求,但 Asynchronix 是一个通用目的的离散事件模拟器,预计适用于广泛的模拟活动。它借鉴了太空飞行器实时模拟器的经验,但在许多方面与太空行业现有工具不同,包括
- 性能:通过利用 Rust 对多线程和异步编程的优秀支持,模拟模型可以高效并行运行,所有必要的同步都由模拟器透明处理,
- 开发者友好性:符合人体工程学的 API 和 Rust 对代数类型的支持使其非常适合物理信息中的“信息”部分,即用于具有甚至非常复杂状态机的数字设备建模,
- 开源:最后但并非最不重要的是,Asynchronix 在 MIT 和 Apache 2 许可证下分发,明确旨在促进一个生态系统,其中模型可以轻松交换,而无需依赖专有 API。
文档
API 文档相对详尽,包括一个实用的概述,应该提供所有必要的起始信息。
还可以在专门的 目录 中找到更多实例。
用法
将以下内容添加到您的 Cargo.toml
[dependencies]
asynchronix = "0.2.2"
示例
// A system made of 2 identical models.
// Each model is a 2× multiplier with an output delayed by 1s.
//
// ┌──────────────┐ ┌──────────────┐
// │ │ │ │
// Input ●─────▶│ multiplier 1 ├─────▶│ multiplier 2 ├─────▶ Output
// │ │ │ │
// └──────────────┘ └──────────────┘
use asynchronix::model::{Model, Output};
use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::time::{MonotonicTime, Scheduler};
use std::time::Duration;
// A model that doubles its input and forwards it with a 1s delay.
#[derive(Default)]
pub struct DelayedMultiplier {
pub output: Output<f64>,
}
impl DelayedMultiplier {
pub fn input(&mut self, value: f64, scheduler: &Scheduler<Self>) {
scheduler
.schedule_event(Duration::from_secs(1), Self::send, 2.0 * value)
.unwrap();
}
async fn send(&mut self, value: f64) {
self.output.send(value).await;
}
}
impl Model for DelayedMultiplier {}
// Instantiate models and their mailboxes.
let mut multiplier1 = DelayedMultiplier::default();
let mut multiplier2 = DelayedMultiplier::default();
let multiplier1_mbox = Mailbox::new();
let multiplier2_mbox = Mailbox::new();
// Connect the output of `multiplier1` to the input of `multiplier2`.
multiplier1
.output
.connect(DelayedMultiplier::input, &multiplier2_mbox);
// Keep handles to the main input and output.
let mut output_slot = multiplier2.output.connect_slot().0;
let input_address = multiplier1_mbox.address();
// Instantiate the simulator
let t0 = MonotonicTime::EPOCH; // arbitrary start time
let mut simu = SimInit::new()
.add_model(multiplier1, multiplier1_mbox)
.add_model(multiplier2, multiplier2_mbox)
.init(t0);
// Send a value to the first multiplier.
simu.send_event(DelayedMultiplier::input, 3.5, &input_address);
// Advance time to the next event.
simu.step();
assert_eq!(simu.time(), t0 + Duration::from_secs(1));
assert_eq!(output_slot.take(), None);
// Advance time to the next event.
simu.step();
assert_eq!(simu.time(), t0 + Duration::from_secs(2));
assert_eq!(output_slot.take(), Some(14.0));
实现说明
在内部,Asynchronix基于异步实现的actor模型,其中每个仿真模型都是一个actor。模型之间实际交换的消息是async
闭包,它捕获事件或请求的值并将模型作为&mut self
参数。与模型相关联并接收闭包的邮箱是一个异步、有界MPSC通道的接收者。
计算在离散时间进行。当模型执行时,它们可以请求调度器在特定的仿真时间发送一个事件(或者更准确地说,是一个捕获该事件的闭包)。每当当前时间的计算完成时,调度器会选择下一个安排了事件的时间点(下一个事件增量),从而触发另一组计算。
这种计算过程使得使用通用异步运行时,如Tokio变得困难,因为一系列计算的结束在技术上是一个死锁:计算完成时,所有模型都没有剩余的事情要做,并且被空邮箱阻塞。此外,运行时管理的是一个包含已发布事件的优先队列,而不是管理传统的reactor。因此,Asynchronix依赖于一个完全定制的运行时。
尽管运行时在很大程度上受到Tokio的影响,但它具有额外的优化,使其在离散事件模拟中常见的大量消息传递工作负载上比任何其他多线程Rust执行器都快(参见基准测试)。Asynchronix还通过一个非常快的自定义MPSC通道改进了现有技术,其性能通过Tachyonix得到了证明,这是该通道的通用分支。
许可证
此软件根据您的选择,受Apache许可证,版本2.0或MIT许可证的许可。
贡献
除非您明确声明,否则根据Apache-2.0许可证定义的任何有意提交的工作,包括您的工作,都应按照上述方式双重许可,没有其他条款或条件。
依赖关系
~0.5–30MB
~380K SLoC