我应该通过引用还是值传递一个大数组?

Should I pass a large array by reference or value?

我正在用 Rust 构建一个 Game Boy 模拟器。我创建了一个 readByteFromMemory() 函数,该函数将一个 8KB 数组作为参数,该数组代表 Game Boy 的 8KB 内部存储器。我在这里考虑两个选项:

fn readByteFromMemory(internalRAM: [u8;0x2000]) -> u8

fn readByteFromMemory(internalRAM: &[u8;0x2000]) -> u8

我能找到的唯一引用此主题的是这篇文章:http://words.steveklabnik.com/pointers-in-rust-a-guide 其中指出:

But don’t reach for that pointer until you must! Make sure that the 
struct is large enough by performing some tests before you add in the 
complexity of pointers.

我 运行 对这两个选项进行了基准测试,似乎无论优化器是打开还是关闭,通过指针传递的速度都快得多(正如预期的那样)。

不过有趣的是,如果关闭优化器,按值传递会稍微快一些,如下所示:

➜  rustTest$  rustc --test -O passByTest.rs
➜  rustTest$  ./passByTest --bench         

running 2 tests
test ptrBench   ... bench:         1 ns/iter (+/- 0)
test valueBench ... bench:       221 ns/iter (+/- 2)

test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured

➜  rustTest$  rustc --test  passByTest.rs 
➜  rustTest$  ./passByTest --bench       

running 2 tests
test ptrBench   ... bench:        13 ns/iter (+/- 3)
test valueBench ... bench:       152 ns/iter (+/- 1)

我的问题是:按引用传递比按值传递有意义的字节阈值是多少?

我无法直接回答您的问题,因为与许多其他问题一样,这取决于代码的位置、代码周围的代码在做什么、访问模式是什么等等。如果您关心性能,您必须分析

也就是说...

  • "Large" 在 Rust 中按值传递的值 实际上 通过引用传递作为优化。通过 "large",我相信它比几个指针更大。同样,这是一个优化,因此您依赖编译器在这里做出合理的选择。在大多数情况下,它应该选择正确。

    还要记住,您自己不能"pass logically by value, actually by reference"(编译器正在做的事情);这意味着使用 &move 参考,该语言中尚不存在。

    我认为史蒂夫的评论应该被更自由地解释为:"don't override the compiler yourself unless profiling tells you to."

  • 那么两者有什么区别吗?是的!如果你传递一个 [u8; 0x2000],那么调用者必须在传递它之前制作一个 copy 的值,假设你希望以后能够再次使用它。这可能是性能差异的来源。

    请注意,如果您将值移动到函数中,编译器不会制作副本。这包括所有不可复制的类型(如 BoxStringVec),并且 应该 包括编译器知道你不是的可复制类型不会再用了。

合适的选择是 &[u8; 0x2000] 因为:

  • 你只需要读取值(如果你想修改它,你需要一个&mut [u8; 0x2000].
  • 您不想获得该值的所有权(在这种情况下,您将按值传递)。

再次强调:不要开始用"this is a big value therefore I will use pass-by-reference"玩游戏,因为编译器已经这样做了,你真的很难搞定手动正确。