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过程宏

Download history 4085/week @ 2024-01-10 4299/week @ 2024-01-17 5738/week @ 2024-01-24 5920/week @ 2024-01-31 4404/week @ 2024-02-07 5897/week @ 2024-02-14 8595/week @ 2024-02-21 8357/week @ 2024-02-28 7161/week @ 2024-03-06 6847/week @ 2024-03-13 6152/week @ 2024-03-20 8098/week @ 2024-03-27 9702/week @ 2024-04-03 7846/week @ 2024-04-10 11276/week @ 2024-04-17 8178/week @ 2024-04-24

38,976 每月下载量
18 个Crates中使用了(直接使用7个)

MIT/Apache

21KB
168

unroll

使用类似属性的宏展开具有整数字面量边界的for循环。

On crates.io On docs.rs Build status

此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]

相对性能差异可以用小提琴图来可视化。

Violin plot for Matrix-Vector product benchmark

不良用法

也有一些地方,这个宏没有预期的效果。例如,当计算大型数组中元素的总和时,可以将计算分解成独立的块,以允许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的作者,为这个问题的初始优秀解决方案。

许可证

本存储库根据您选择受以下任一许可证的约束

之一。

依赖关系

~1.5MB
~33K SLoC