我应该将一个大数组传递给函数时使用引用还是值传递?

7

我正在使用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)

我的问题是:在字节阈值上,通过传递引用比传递值更有意义?
1个回答

4
我不能直接回答你的问题,因为和许多其他事情一样,这取决于代码在哪里、周围的代码正在做什么、访问模式是什么等等。如果你关心性能,你需要进行性能分析。
话虽如此...
在Rust中,按值传递的“大”值实际上作为优化而被引用传递。我认为,“大”的定义是指大于几个指针的任何东西。同样,这是一种优化,所以你要依赖编译器在这里做出合理的选择。在大多数情况下,它应该会正确地选择。
还要记住,你不能自己“逻辑上按值传递,实际上按引用传递”(这就是编译器在做什么),这将意味着使用&move引用,但该语言中不存在这种引用。
我认为Steve的评论应该更加自由地解释为:“除非性能分析告诉你这样做,否则不要自己覆盖编译器。”
那么这两者之间有什么区别吗?有!如果你传递一个[u8; 0x2000],那么调用者在传递它之前必须对该值进行复制,假设你希望稍后再次使用它。这很可能是性能差异的原因。
请注意,如果你将值移动到函数中,编译器不会进行复制。这包括所有不可复制类型(如Box、String和Vec),以及编译器知道你不会再次使用的可复制类型。
适当的选择是&[u8; 0x2000],因为:
- 你只需要从该值中读取(即如果你想修改它,你需要一个&mut [u8; 0x2000])。 - 你不想拥有该值的所有权(在这种情况下,你会按值传递)。
再次强调:不要开始玩“这是一个大值,因此我将使用引用传递”,因为编译器已经在这样做了,而且手动正确地完成很困难。

1
关于第二点:理论上,当可复制的值在函数调用后未被使用时,编译器应该能够像移动一样操作。然而,像其他优化一样,这取决于LLVM是否意识到它是合法的,我不确定rustc当前是否正确地传达了这一点(我认为llvm.lifetime.end放置过于保守存在问题)。 - user395760
我还想补充一点,适当的选择可能是&[u8]而不是&[u8; 0x2000] - Vladimir Matveev
但是编译器不应该知道,因为它被传递为不可变的,所以数组应该是“按逻辑值传递,实际上是按引用传递”吗?除非我漏掉了什么,否则在这里似乎不需要完全复制。 - DanB91
1
@DanB91:编译器只知道 [u8; 0x2000] 是按值逻辑传递的。这意味着你可能会修改它。所以它必须进行预防性复制。&[u8; 0x2000] 明确告诉它,你绝对不会修改你传递的东西,因此它不需要进行复制。 - DK.
@DK. 当值为“Copy”时,拥有权是什么意思? - sanket1729
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接