克隆语句是否被优化过?

Is a clone statement ever optimised out?

我有类似下面的代码

let x = Arc::new(Mutex::new(Thing::new()));

work_on_data(x.clone());
do_more_work_on_data(x.clone());

x 在第二个函数之后不使用,因此不需要第二个克隆。我应该手动删除 clone() 还是对其进行优化?

克隆可能会打印出某些内容或写入文件。优化克隆会改变行为,因此无法完成。

当然,如果编译器完全了解克隆在做什么,并且可以计算出它永远不会有副作用,它可能会优化它,但不要屏住呼吸。

作为一般规则,如果您知道一个函数总是 return 相同的值,请将该值缓存在一个变量中,而不是多次调用一个函数。

为什么不呢?

优化编译器的首要原则是 as-if 规则,它指定任何东西都可以优化,只要编译器可以证明优化不是可观察到。

注意:这是在某些允许特定优化的语言之上的。

例如:

#[derive(Clone, Debug)]
struct MyDummyType(u64);

extern {
    fn print_c(_: *const ());
}

#[inline(never)]
fn print(dummy: MyDummyType) {
    unsafe { print_c(&dummy as *const _ as *const _) }
}

fn main() {
    let x = MyDummyType(42);
    print(x.clone());
    print(x.clone());
}

产生以下内容main

; Function Attrs: nounwind uwtable
define internal void @_ZN8rust_out4main17h0c6f2596c7f28a79E() unnamed_addr #1 {
entry-block:
  tail call fastcc void @_ZN8rust_out5print17h1f2d1a86beea10d7E(i64 42)
  tail call fastcc void @_ZN8rust_out5print17h1f2d1a86beea10d7E(i64 42)
  ret void
}

编译器完全看穿了我们的代码(实际上我不得不使用一个外部函数来强制它在 main 中发出 一些 代码)。


那么,你的情况呢?

老实说,这更难。

具体而言,由于 Drop:

,语义可能发生变化
  • with do_more_work_on_data(x.clone()), x 保证在 执行结束后 被丢弃,因此 Drop 的任何副作用到在当前函数
  • 结束时执行
  • with do_more_work_on_data(x), x 可能do_more_work_on_data 的末尾被丢弃,或者它可能被丢弃在更早的某个地方。

所以为了证明优化不可观察,编译器必须证明:

  • 或者Drop没有效果,
  • 或者说Drop会在do_more_work_on_data的最后执行,跟紧跟其后的一样,
  • 或...?

这可能性有多大?

MutexDrop 实现需要调用 FFI,因此从优化器的角度来看,它 具有 可观察到的效果。

所以这一切都取决于 do_more_work_on_data 是否被内联。如果是这样,是的,确实可以优化额外的 clone 。如果没有,我不会屏住呼吸。