4个版本
0.1.5 | 2020年6月10日 |
---|---|
0.1.4 | 2019年8月7日 |
0.1.3 | 2019年8月6日 |
0.1.2 | 2019年8月6日 |
#377 在 过程宏 中
38,976 每月下载量
在 18 个Crates中使用了(直接使用7个)
21KB
168 行
unroll
使用类似属性的宏展开具有整数字面量边界的for循环。
此crate提供了一个类似属性的宏 unroll_for_loops
,它可以应用于函数。此宏寻找要展开的循环,并在编译时展开它们。
为什么使用过程宏?
已经有一个名为Crunchy的crate,它使用macro_rules!
宏展开循环。此crate提供了一种使用类似属性的宏来展开循环的替代方案。对于使用类似属性的宏展开循环的一些好处:
- 宏调用不会将注释污染到算法中。每个函数只需要一个注释。
- 可以使用宏调用外部定义的变量。
- 可以扩展为部分循环展开
用法
只需在您想展开for循环的函数上方添加 #[unroll_for_loops]
即可。目前所有具有整数字面量边界的for循环都将展开,尽管此宏目前无法查看复杂代码(例如闭包内的for循环)。
示例
以下示例演示了可以自动展开的代码类型。
#[unroll_for_loops]
fn main() {
println!("matrix = ");
for i in 0..10 {
for j in 0..10 {
print!("({:?}, {:?})", i, j);
}
println!("");
}
}
良好用途
当然,并非所有代码都从展开中受益。例如,上面的代码受I/O限制,因此可能不会从展开中受益。
另一方面,计算任务,如矩阵乘法,可以通过以下简单的矩阵-向量积示例显著受益
#[unroll_for_loops]
fn mtx_vec_mul(mtx: &[[f64; 5]; 5], vec: &[f64; 5]) -> [f64; 5] {
let mut out = [0.0; 5];
for col in 0..5 {
for row in 0..5 {
out[row] += mtx[col][row] * vec[col];
}
}
out
}
使用criterion生成的代码的快速基准测试显示,循环展开将性能提高了3倍
Matrix-Vector Product/Ordinary
time: [2.7254 ns 2.7657 ns 2.8153 ns]
Matrix-Vector Product/Unrolled
time: [878.71 ps 882.96 ps 888.01 ps]
相对性能差异可以用小提琴图来可视化。
不良用法
也有一些地方,这个宏没有预期的效果。例如,当计算大型数组中元素的总和时,可以将计算分解成独立的块,以允许CPU执行乱序执行优化。例如以下代码
fn sum(data: &[f64]) -> f64 {
let mut sum = 0.0;
for i in 0..data.len() {
sum += data[i];
}
sum
}
当分块计算时,可以获得显著的性能提升
fn sum(data: &[f64]) -> f64 {
let mut sum = [0.0; 32];
for i in (0..data.len()).step_by(32) {
for j in 0..32 {
sum[j] += data[i + j];
}
}
sum.iter().sum()
}
然而,这个例子不太可能从unroll_for_loops
宏中受益。包含展开求和的基准测试已包含以供验证。
话虽如此,像上面那样可以部分展开循环的宏非常有用,留待未来工作。
欢迎贡献力量!
致谢
我想感谢Crunchy的作者,为这个问题的初始优秀解决方案。
许可证
本存储库根据您选择受以下任一许可证的约束
- Apache许可证2.0版本,(LICENSE-APACHE或https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT许可证(LICENSE-MIT或https://opensource.org/licenses/MIT)
之一。
依赖关系
~1.5MB
~33K SLoC