19 个版本 (5 个稳定版)
| 2.1.1 | 2024年3月20日 |
|---|---|
| 2.0.1 | 2024年1月30日 |
| 1.0.0 | 2024年1月15日 |
| 0.6.1 | 2023年12月6日 |
| 0.5.1 | 2023年10月5日 |
#689 在 科学
每月167次 下载
用于 16 个crate(通过 gmt_dos-actors)
68KB
1.5K SLoC
actorscript
GMT DOS Actors 的脚本微语言。
actorscript 过程宏是一个 专用领域语言,用于编写 gmt_dos-actors 模型。
actorscript 解析 流程。一个 流程 由采样率后跟一个 链 组成。一个 链 是由演员的客户端和输出组成的序列,它们之间由分隔符 -> 分隔。
例如
actorscript! {
1: a[A2B] -> b
};
是一个连接客户端 a 的输出 A2B 到客户端 b 的输入的 流程,该输入在标称采样率下。此示例将由编译器扩展为
let mut a: Actor<_,1,1> = a.into();
let mut b: Actor<_,1,1> = b.into();
a.add_output().build::<A2B>().into_input(&mut b)?;
let model = model!(a,b).name("model").flowchart().check()?;
要使上述代码成功编译,必须为客户端 a 和 b 分别实现 traits Write<A2B> 和 Read<A2B>。
gmt_dos-actors 模型以 completed 状态编写,这意味着模型将自动运行到完成
模型写入的状态可以通过 model 属性的 state 参数进行更改。除了 completed,还可以指定两种其他状态
ready
actorscript! {
#[model(state = ready)]
1: a[A2B] -> b
};
将构建和检查模型,但运行模型和等待模型完成由用户通过调用
model.run().await?;
正在运行
actorscript! {
#[model(state = running)]
1: a[A2B] -> b
};
将执行模型,等待模型完成将由用户通过调用
model.await?;
- 和
completed
actorscript! {
#[model(state = completed)]
1: a[A2B] -> b
};
将执行模型并等待其完成。
客户端被其同名演员消耗,在 actorscript 之后不再可用。如果需要在 actorscript 之后仍然访问客户端,可以在客户端之前插入令牌 &,例如。
actorscript! {
#[model(state = completed)]
1: a[A2B] -> &b
};
在这里,客户端 b 被封装到一个 Arc<Mutex<_>> 容器中,复制并传递给相关的演员。稍后可以通过以下方式检索客户端 b 的引用
let b_ref = b.lock().await.deref();
模型增长
模型通过扩展 链 并添加新的 流 来增长。
一个 链 通过添加新的客户端和输出增长,例如。
actorscript! {
1: a[A2B] -> b[B2C] -> c
};
其中输出 B2C 被添加到 b 并连接到客户端 c。
通过以下方式添加一个新的 流
actorscript! {
1: a[A2B] -> b[B2C] -> c
10: c[C2D] -> d
};
在这里,新的 流 以名义采样率的 1/10 采样率进行下采样。
可以通过类似的方式获得上采样
actorscript! {
1: a[A2B] -> b[B2C] -> c
10: c[C2D] -> d
5: d[D2E] -> e
};
在上面的模型中,C2D 每 10 个样本从 c 发送到 d,而 D2E 在 10 个样本的间隔内连续两次从 d 发送到 e。
下表给出了每个客户端的输入和输出的采样率
a |
b |
c |
d |
e |
|
|---|---|---|---|---|---|
| 输入 | 0 | 1 | 1 | 10 | 5 |
| 输出 | 1 | 1 | 10 | 5 | 0 |
速率转换
前面的例子说明了速率转换可以通过依赖于演员内部的上采样和下采样实现“自然”地发生在客户端之间。然而,只有当客户端的输入和/或输出在每次 流 中只使用一次时,这才能工作。
考虑以下示例
actorscript! {
1: a[A2B] -> b[B2C] -> d
10: c[C2D] -> b
};
在这种情况下,输入和输出采样率表如下
a |
b |
c |
d |
|
|---|---|---|---|---|
| 输入 | 0 | 1 | 0 | 1 |
| 输出 | 1 | 1 | 10 | 0 |
在这里,C2D 输出以 1/10 的采样率与继承自第一个 流 的采样率为 1 的 b 输入之间存在不匹配。
actorscript 能够检测到这种不匹配,并在 c 和 b 之间引入一个速率转换客户端,有效地重写了模型,如下所示
actorscript! {
1: a[A2B] -> b[B2C] -> d
10: c[C2D] -> r
1: r[C2D] -> b
};
其中 r 是上采样率转换客户端 Sampler。
反馈循环
反馈循环的一个例子是 流 内部的封闭 链,例如。
actorscript! {
1: a[A2B] -> b[B2C] -> c[C2B]! -> b
};
数据流由最左边的客户端(a)启动,而 b 将阻塞,直到它收到 A2B 和 C2B,但 c 无法发送 C2B,直到他从 b 收到 B2C,因此 b 和 c 都在互相等待。为了打破这种僵局,可以指示一个客户端立即发送给定输出的数据,通过在输出后附加标记 ! 来实现。
在上面的示例中,c 在 a 发送 A2B 的同时发送 C2B,因此允许 b 继续进行。
另一个跨越2个流的反馈循环的示例
actorscript! {
1: a[A2B] -> b[B2C] -> c
10: c[C2D]! -> d[D2B] -> b
};
这个版本也会正常工作
actorscript! {
1: a[A2B] -> b[B2C] -> c
10: c[C2D] -> d[D2B]! -> b
};
输出数据记录
在输出后附加标记 $ 触发记录输出数据,如下所示
actorscript! {
1: a[A2B]$ -> b[B2C]$ -> c
10: c[C2D]$ -> d[DD]$
};
其中,A2B 和 B2C 输出数据记录到 parquet 文件 data_1.parquet,而 C2D 和 DD 输出数据记录到 parquet 文件 data_10.parquet。为了记录的目的,actorscript 将模型重写为
actorscript! {
1: a[A2B] -> b[B2C] -> c
10: c[C2D] -> d[DD]
1: a[A2B] -> l1
1: b[B2C] -> l1
10: c[C2D] -> l10
10: d[DD] -> l10
};
其中,l1 和 l10 是两个 Arrow 记录客户端。
依赖关系
~0.8–1.4MB
~30K SLoC