5 个版本
0.0.5 | 2024 年 4 月 6 日 |
---|---|
0.0.4 | 2024 年 4 月 1 日 |
0.0.3 | 2024 年 3 月 20 日 |
0.0.2 | 2024 年 3 月 20 日 |
0.0.1 | 2024 年 3 月 19 日 |
#253 在 过程宏
2,821 每月下载量
17KB
315 行
Const-currying
一个 Rust proc macro,帮助你使用柯里化技术提高函数的性能。
动机
Const generics 是 Rust 的一项功能,允许你将 const 值作为泛型参数传递。此功能非常有助于提高代码性能,通过在编译时生成同一函数的多个版本。然而,有时即使很可能是已知的 const 值,或者调用的一部分使用 const 值,你也必须使用运行时依赖的值来调用函数。此宏可以帮助你在编译时生成具有 const 或运行时值的同一函数的多个版本。
有一个令人兴奋的 crate partial_const,它已经提供了类似的功能。它完全基于 Rust 的奇妙类型系统,但是它要求调用者显式指定 const 值。我们的 crate 提供了一个基于 proc-macro 的解决方案,它对调用者的代码不引入任何侵入性更改。
用法
您可以使用 maybe_const
属性指定函数参数的潜在 const 值。
use const_currying::const_currying;
#[const_currying]
fn f1(
#[maybe_const(dispatch = x, consts = [0, 1])] x: i32,
#[maybe_const(dispatch = y, consts = [true, false])] y: bool,
z: &str,
) -> i32 {
if y {
x
} else {
-x
}
}
有两个参数 x
和 y
,它们可能以 const 值的形式传递。可选的 dispatch
属性指定生成的函数名的后缀。以下是一个示例,我们可以在这里看到生成的完整代码。
#[allow(warnings)]
fn f1_orig(x: i32, y: bool, z: &str) -> (i32, String) {
if y { (x, z.to_string()) } else { (-x, z.chars().rev().collect()) }
}
#[allow(warnings)]
fn f1_x<const x: i32>(y: bool, z: &str) -> (i32, String) {
if y { (x, z.to_string()) } else { (-x, z.chars().rev().collect()) }
}
#[allow(warnings)]
fn f1_y<const y: bool>(x: i32, z: &str) -> (i32, String) {
if y { (x, z.to_string()) } else { (-x, z.chars().rev().collect()) }
}
#[allow(warnings)]
fn f1_x_y<const x: i32, const y: bool>(z: &str) -> (i32, String) {
if y { (x, z.to_string()) } else { (-x, z.chars().rev().collect()) }
}
#[inline(always)]
fn f1(x: i32, y: bool, z: &str) -> (i32, String) {
match (x, y) {
(1, false) => f1_x_y::<1, false>(z),
(1, true) => f1_x_y::<1, true>(z),
(0, false) => f1_x_y::<0, false>(z),
(0, true) => f1_x_y::<0, true>(z),
(x, false) => f1_y::<false>(x, z),
(x, true) => f1_y::<true>(x, z),
(1, y) => f1_x::<1>(y, z),
(0, y) => f1_x::<0>(y, z),
(x, y) => f1_orig(x, y, z),
}
}
原始函数 f1
被重命名为 f1_orig
,并生成两个 const 参数 x
和 y
的幂集作为不同的函数 f1_x
、f1_y
和 f1_x_y
。最后,原始函数 f1
被替换为一个调度函数,该函数根据 x
和 y
的运行时值调用生成的函数。
优势
在大多数情况下,编译器的优化已经足够可靠,能够为你生成最佳代码。然而,当你的函数过于复杂以至于无法内联时,编译器可能无法获取足够的信息来优化该函数。
该宏会生成函数的多个版本,并在分发函数中显式匹配 consts 参数。这迫使编译器为每个 const 值生成最佳代码。这也是 Rust 中引入 const-generics 的原因,这个宏使得与运行时依赖值的使用变得更加容易。
"没有银弹" 是软件工程中众所周知的原则。该宏不是银弹,而且并不总是使用它的最佳选择。至少,它可能会大幅增加你的二进制文件大小。在使用该宏之前和之后,你应该始终对代码进行性能分析,并确保性能有所提升。
许可证
根据您的选择,许可协议为 Apache License, Version 2.0 或 MIT 许可证。
依赖
~1–1.5MB
~33K SLoC