【问题标题】:Can I efficiently return an object by value in Rust?我可以在 Rust 中有效地按值返回对象吗?
【发布时间】:2015-01-08 07:55:29
【问题描述】:

我想用函数初始化一个大对象。目前我有:

fn initialize(mydata: &mut Vec<Vec<MyStruct>>) { /* ... */ }

我希望有:

fn initialize() -> Vec<Vec<MyStruct>> { /* ... */ }

我听说 C++ 经常实现返回值优化 (RVO),如果你很幸运并且有一个好的编译器的话。我们可以在这里禁用复制并让它通过传递给函数的隐藏指针返回吗? RVO 是语言的一部分还是可选的优化?

【问题讨论】:

  • This discussion 建议 RVO 触发大于指针的任何内容。但也有关于 NRVO 的 this open issue。所以我想这取决于你如何实现initialize

标签: optimization rust return-value-optimization


【解决方案1】:

是的,无论如何,你应该写

fn initialize() -> Vec<Vec<MyStruct>> { ... }

(顺便说一句,Vec 并没有那么大——它只有 3 个指针大小的整数)

Rust 有 RVO,还有这个 is advertised in guides。您可以使用以下代码自己查看:

#[inline(never)]
fn initialize() -> Vec<i32> {
    Vec::new()
}

fn main() {
    let v = initialize();
}

如果你在发布模式on the playground 下编译这个程序,输出程序集,你会看到:

playground::initialize:
    movq    $4, (%rdi)
    xorps   %xmm0, %xmm0
    movups  %xmm0, 8(%rdi)
    retq

Vec::new() 被内联了,但是你可以看到这个想法——将新的Vec 实例的地址传递给%rdi 中的函数,函数将Vec 字段直接存储到这个内存中,避免了不必要的通过堆栈复制。它是这样称呼的:

playground::main:
    subq    $24, %rsp
    movq    %rsp, %rdi
    callq   playground::initialize

可以看到最终Vec实例会直接放入栈内存中。

【讨论】:

  • 可能,但由于某种原因,我发现 LLVM IR 读取比程序集更难:(
  • 为此我们只需要签名:define internal fastcc void @initialize(%"struct.collections::vec::Vec&lt;[i32]&gt;[#3]"* noalias nocapture sret dereferenceable(24)) unnamed_addr #0。 LLVM IR 与 C 的声明类似,因此该函数返回 void 并将指针 (*) 指向 struct.collections::vec::Vec&lt;[i32]&gt;。 (我使用#[no_mangle] 使其更清晰。)
  • 这可能显示传递了一个指针,但如果您查看 LLVM IR 的末尾,您可能会看到该内存块或类似的 memcpy。我写了一段代码,它分配了一个 256 int 数组来返回,如果你查看 IR 的底部,你会看到从一个对象到另一个对象的副本。查看生成的 ASM(play.r-l.org 上的 beta 版和 nightly 版),您会在函数调用的底部看到对 mempcy@PLT 的调用。 is.gd/u9GVx6
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-09
  • 2022-01-19
  • 1970-01-01
  • 2016-03-18
  • 2020-04-26
  • 1970-01-01
  • 2017-05-21
相关资源
最近更新 更多