【问题标题】:Does partial application in Rust have overhead?Rust 中的部分应用程序是否有开销?
【发布时间】:2018-03-14 21:52:44
【问题描述】:

我喜欢使用部分应用程序,因为它允许(除其他外)拆分复杂的函数调用,这样更具可读性。

部分应用示例:

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn main() {
    let add7 = |x| add(7, x);

    println!("{}", add7(35));
}

这种做法有开销吗?

这是我喜欢做的事情(来自真实代码):

fn foo(n: u32, things: Vec<Things>) {
    let create_new_multiplier = |thing| ThingMultiplier::new(thing, n); // ThingMultiplier is an Iterator
    let new_things = things.clone().into_iter().flat_map(create_new_multiplier);

    things.extend(new_things);
}

这纯粹是视觉效果。我不喜欢太多的东西。

【问题讨论】:

  • 一般来说,LLVM 在消除此类抽象开销方面做得非常好。但是,如果您不确定,通常最好的办法是运行一些基准测试!
  • 就最新的编译器而言,该特定示例将编译为println!("{}", 42)。如果您将black_box 输入到add7,您将得到println!("{}", 35 + 7)。您可能需要一个更复杂的示例才能找到确实发生开销的地方。 :)
  • @E_net4 请不要从字面上理解这个例子。这只是部分应用的一个简单示例。
  • 当然可以。另一方面,我们想知道您是否对部分应用程序的使用不那么琐碎,当然,您是否正在考虑替代实现或基线(例如,具有相同功能但没有闭包的东西部分应用)。
  • @E_net4 已编辑。

标签: rust closures


【解决方案1】:

在使用之前定义闭包与直接定义和使用闭包之间应该没有性能差异。存在类型系统差异——the compiler doesn't fully know how to infer types in a closure that isn't immediately called

在代码中:

let create_new_multiplier = |thing| ThingMultiplier::new(thing, n);
things.clone().into_iter().flat_map(create_new_multiplier)

将与

完全相同
things.clone().into_iter().flat_map(|thing| { 
    ThingMultiplier::new(thing, n)
})

一般来说,使用闭包不应该有性能成本。这就是 Rust 所说的“零成本抽象”的意思:程序员自己写得再好不过了。

编译器converts a closure into implementations of the Fn* traits on an anonymous struct。到那时,所有正常的编译器优化都开始了。由于像单态化这样的技术,它甚至可能更快。这确实意味着您需要进行常规分析以查看它们是否是瓶颈。

【讨论】:

    【解决方案2】:

    在您的特定示例中,是的,extend 可以作为循环内联,其中包含 flat_map 的另一个循环,该循环又将 ThingMultiplier 实例放入包含 nthing 的相同堆栈槽中。

    但是你在这里吠叫错误的效率树。与其想知道一个包含两个字段的小型结构的分配是否会被优化掉,不如想知道clone 的效率有多高,尤其是对于大型输入。

    【讨论】:

      猜你喜欢
      • 2019-07-24
      • 2014-03-22
      • 1970-01-01
      • 1970-01-01
      • 2011-06-15
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 2013-05-06
      相关资源
      最近更新 更多