2 个不稳定版本
0.2.0 | 2023年1月16日 |
---|---|
0.1.0 | 2023年1月11日 |
#46 in #dispatch
在 union-fn 中使用
70KB
968 行
[2]: https://github.com/Robbepop/union-fn/actions/workflows/rust.yml [4]: https://docs.rs/union-fn [6]: https://crates.io/crates/union-fn
持续集成 | 文档 | Crates.io |
---|---|---|
[][2] | [][4] | [][6] |
#[union_fn]
:高效解释器的数据结构
此软件包提供了一个过程宏 #[union_fn]
,可以应用于 Rust trait
定义。
#[union_fn]
可以被视为一组多态、参数化的函数,这些函数针对数据局部性和多态调用进行了优化。
动机与想法
解释器通常使用基于 switch-loop
的指令调度,其中简单的 enum
代表所有不同类型的指令,如 Add
和 Branch
。指令调度发生在指令执行之间,当使用这种形式的调度通过分支表时,通常会存在大量的开销,而这种分支表通常不会被理想地优化。
#[union_fn]
宏通过将函数指针直接嵌入到其函数参数旁边的类型中,将调度成本降低到最小。这样,就不需要分支表,调用调度等于间接函数调用。
由于Rust enum
的区分符对齐,enum
表示形式浪费了很多空间,而这些空间被优化表示正确利用,通过存储函数指针而不是enum
的区分符。因此,这两种类型通常具有相同的size_of
。指向的函数知道如何解码通过union
编码的函数参数,且没有额外开销。
代码生成
#[union_fn]
宏主要生成两种不同的类型
- 使用特质的标识符引用的所有特质的枚举表示。
- 用于检查、调试和创建不同的调用。
- 通过特质的标识符访问,例如
Foo
。 - 每个方法生成一个具有相同名称和参数的构造函数。
- 一种优化数据局部性和多态调用的类型。
- 主要用于计算阶段的实际调用。
- 通过
<Foo as union_fn::IntoOpt>::Opt>
访问,其中Foo
是特质的标识符。 - 每个方法生成一个具有相同名称和参数的构造函数,或者可以通过
union_fn::IntoOpt
特质从enum
表示形式转换。
示例
解释器
一个完整的计算器示例,它可以作为解释器的灵感来源,可以在这里找到。
代码生成
给定以下Rust代码
use union_fn::union_fn;
#[union_fn]
trait Counter {
type Context = i64;
/// Bumps the value `by` the amount.
fn bump_by(value: &mut Self::Context, by: i64) {
*value += by;
}
/// Selects the values in `choices` depending on `value`.
fn select(value: &mut Self::Context, choices: [i64; 4]) {
*value = choices.get(*value as usize).copied().unwrap_or(0)
}
/// Resets the `value` to zero.
fn reset(value: &mut Self::Context) {
*value = 0;
}
}
fn main() {
let mut value = 0;
Counter::bump_by(1).call(&mut value);
assert_eq!(value, 1);
Counter::bump_by(41).call(&mut value);
assert_eq!(value, 42);
Counter::reset().call(&mut value);
assert_eq!(value, 0);
let choices = [11, 22, 33, 44];
let opt = Counter::select(choices).into_opt();
for i in 0..5 {
let mut value = i;
opt.call(&mut value);
assert_eq!(value, choices.get(i as usize).copied().unwrap_or(0));
}
}
此过程宏将扩展为以下代码(注意,为了演示目的,空格和derive宏扩展已更改。)
#[derive(::core::marker::Copy, ::core::clone::Clone)]
pub enum Counter {
/// Bumps the value `by` the amount.
BumpBy { by: i64 },
/// Selects the values in `choices` depending on `value`.
Select { choices: [i64; 4] },
/// Resets the `value` to zero.
Reset {},
}
impl Counter {
/// Bumps the value `by` the amount.
pub fn bump_by(by: i64) -> Self {
Self::BumpBy { by }
}
/// Selects the values in `choices` depending on `value`.
pub fn select(choices: [i64; 4]) -> Self {
Self::Select { choices }
}
/// Resets the `value` to zero.
pub fn reset() -> Self {
Self::Reset {}
}
}
impl ::union_fn::CallWithContext for Counter {
type Context = i64;
fn call(
self,
ctx: &mut Self::Context,
) -> <Counter as ::union_fn::UnionFn>::Output {
match self {
Self::BumpBy { by } => {
<Self as ::union_fn::IntoOpt>::Impls::bump_by(ctx, by)
}
Self::Select { choices } => {
<Self as ::union_fn::IntoOpt>::Impls::select(ctx, choices)
}
Self::Reset { } => {
<Self as ::union_fn::IntoOpt>::Impls::reset(ctx,)
}
}
}
}
const _: () = {
///Call optimized structure of the [`Counter`] type.
#[derive(::core::marker::Copy, ::core::clone::Clone)]
pub struct CounterOpt {
handler: fn(
ctx: &mut <Counter as ::union_fn::CallWithContext>::Context,
&<Counter as ::union_fn::UnionFn>::Args,
) -> <Counter as ::union_fn::UnionFn>::Output,
args: <Counter as ::union_fn::UnionFn>::Args,
}
impl ::union_fn::IntoOpt for Counter {
type Opt = CounterOpt;
type Delegator = CounterDelegate;
type Impls = CounterImpls;
fn into_opt(self) -> Self::Opt {
match self {
Self::BumpBy { by } => <Self as ::union_fn::IntoOpt>::Opt::bump_by(by),
Self::Select { choices } => {
<Self as ::union_fn::IntoOpt>::Opt::select(choices)
}
Self::Reset {} => <Self as ::union_fn::IntoOpt>::Opt::reset(),
}
}
}
impl ::union_fn::CallWithContext for CounterOpt {
type Context = i64;
fn call(
self,
ctx: &mut Self::Context,
) -> <Counter as ::union_fn::UnionFn>::Output {
(self.handler)(ctx, &self.args)
}
}
impl CounterOpt {
/// Bumps the value `by` the amount.
pub fn bump_by(by: i64) -> Self {
Self {
handler: <Counter as ::union_fn::IntoOpt>::Delegator::bump_by,
args: <Counter as ::union_fn::UnionFn>::Args::bump_by(by),
}
}
/// Selects the values in `choices` depending on `value`.
pub fn select(choices: [i64; 4]) -> Self {
Self {
handler: <Counter as ::union_fn::IntoOpt>::Delegator::select,
args: <Counter as ::union_fn::UnionFn>::Args::select(choices),
}
}
/// Resets the `value` to zero.
pub fn reset() -> Self {
Self {
handler: <Counter as ::union_fn::IntoOpt>::Delegator::reset,
args: <Counter as ::union_fn::UnionFn>::Args::reset(),
}
}
}
///Efficiently packed method arguments for the [`Counter`] type.
#[derive(::core::marker::Copy, ::core::clone::Clone)]
pub union CounterArgs {
/// Bumps the value `by` the amount.
bump_by: i64,
/// Selects the values in `choices` depending on `value`.
select: [i64; 4],
/// Resets the `value` to zero.
reset: (),
}
impl CounterArgs {
/// Bumps the value `by` the amount.
pub fn bump_by(by: i64) -> Self {
Self { bump_by: by }
}
/// Selects the values in `choices` depending on `value`.
pub fn select(choices: [i64; 4]) -> Self {
Self { select: choices }
}
/// Resets the `value` to zero.
pub fn reset() -> Self {
Self { reset: () }
}
}
impl ::union_fn::UnionFn for CounterOpt {
type Output = ();
type Args = CounterArgs;
}
impl ::union_fn::UnionFn for Counter {
type Output = ();
type Args = CounterArgs;
}
///Decodes and delegates packed arguments to the implementation of [`Counter`] methods.
pub enum CounterDelegate {}
impl CounterDelegate {
/// Bumps the value `by` the amount.
fn bump_by(
value: &mut <Counter as ::union_fn::CallWithContext>::Context,
args: &<Counter as ::union_fn::UnionFn>::Args,
) -> <Counter as ::union_fn::UnionFn>::Output {
let by = unsafe { args.bump_by };
<Counter as ::union_fn::IntoOpt>::Impls::bump_by(value, by)
}
/// Selects the values in `choices` depending on `value`.
fn select(
value: &mut <Counter as ::union_fn::CallWithContext>::Context,
args: &<Counter as ::union_fn::UnionFn>::Args,
) -> <Counter as ::union_fn::UnionFn>::Output {
let choices = unsafe { args.select };
<Counter as ::union_fn::IntoOpt>::Impls::select(value, choices)
}
/// Resets the `value` to zero.
fn reset(
value: &mut <Counter as ::union_fn::CallWithContext>::Context,
args: &<Counter as ::union_fn::UnionFn>::Args,
) -> <Counter as ::union_fn::UnionFn>::Output {
let () = unsafe { args.reset };
<Counter as ::union_fn::IntoOpt>::Impls::reset(value)
}
}
///Implements all methods of the [`Counter`] type.
pub enum CounterImpls {}
impl CounterImpls {
/// Bumps the value `by` the amount.
fn bump_by(
value: &mut <Counter as ::union_fn::CallWithContext>::Context,
by: i64,
) -> <Counter as ::union_fn::UnionFn>::Output {
*value += by;
}
/// Selects the values in `choices` depending on `value`.
fn select(
value: &mut <Counter as ::union_fn::CallWithContext>::Context,
choices: [i64; 4],
) -> <Counter as ::union_fn::UnionFn>::Output {
*value = choices.get(*value as usize).copied().unwrap_or(0);
}
/// Resets the `value` to zero.
fn reset(
value: &mut <Counter as ::union_fn::CallWithContext>::Context,
) -> <Counter as ::union_fn::UnionFn>::Output {
*value = 0;
}
}
};
生成的汇编代码
以下是针对上述Counter
示例中生成的用户界面enum
和调用优化的opt
类型,根据编译器探索器生成的汇编代码
调用[WithContext] for <enum>
<example::Counter as example::union_fn::CallWithContext>::call:
sub rsp, 40
mov rax, qword ptr [rdi]
test rax, rax
je .LBB3_8
cmp eax, 1
jne .LBB3_6
movups xmm0, xmmword ptr [rdi + 8]
movups xmm1, xmmword ptr [rdi + 24]
movaps xmmword ptr [rsp + 16], xmm1
movaps xmmword ptr [rsp], xmm0
mov rax, qword ptr [rsi]
cmp rax, 3
ja .LBB3_3
mov rax, qword ptr [rsp + 8*rax]
mov qword ptr [rsi], rax
add rsp, 40
ret
.LBB3_8:
mov rax, qword ptr [rdi + 8]
add qword ptr [rsi], rax
add rsp, 40
ret
.LBB3_6:
mov qword ptr [rsi], 0
add rsp, 40
ret
.LBB3_3:
xor eax, eax
mov qword ptr [rsi], rax
add rsp, 40
ret
调用[WithContext] for <opt>
<example::_::CounterOpt as example::union_fn::CallWithContext>::call:
mov rax, rsi
mov rcx, qword ptr [rdi]
lea rsi, [rdi + 8]
mov rdi, rax
jmp rcx
依赖关系
~1.5MB
~36K SLoC