Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I pass a large array by reference or value?

I am building a Game Boy emulator in Rust. I created a readByteFromMemory() function which takes as a parameter an 8KB array which represents the Game Boy's internal memory which was 8KB. I am thinking of two options here:

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

or

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

The only thing I could find that references this topic is this article: http://words.steveklabnik.com/pointers-in-rust-a-guide which states:

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.

I ran a benchmark test on both options and it seems that no matter if the optimizer turned on or off, the pass by pointer one was much faster (as expected).

Though interestingly enough the pass by value was slightly faster if the optimizer was turned off as shown here:

➜  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)

My question is: what seems to be the threshold in bytes where passing by reference makes sense over passing by value?

like image 881
DanB91 Avatar asked Feb 10 '15 04:02

DanB91


People also ask

Is array pass by value or reference?

Longer answer: Like all Java objects, arrays are passed by value ... but the value is the reference to the array. So, when you assign something to a cell of the array in the called method, you will be assigning to the same array object that the caller sees.

Which is better pass by value or pass by reference?

Pass-by-references is more efficient than pass-by-value, because it does not copy the arguments. The formal parameter is an alias for the argument. When the called function read or write the formal parameter, it is actually read or write the argument itself.

Is it faster to pass by reference or value?

As a rule of thumb, passing by reference or pointer is typically faster than passing by value, if the amount of data passed by value is larger than the size of a pointer.

Do you need to pass arrays by reference?

Because arrays are already pointers, there is usually no reason to pass an array explicitly by reference. For example, parameter A of procedure zero above has type int*, not int*&. The only reason for passing an array explicitly by reference is so that you can change the pointer to point to a different array.


1 Answers

I can't answer your question directly because, as with so many other things, it depends on where the code is, what the code around it is doing, what the access patterns are, blah, blah, blah. If you're concerned about performance, you have to profile.

That said...

  • "Large" values passed by value in Rust are actually passed by reference as an optimisation. By "large", I believe it's anything larger than a few pointers. Again, this is an optimisation, so you're relying on the compiler to make a reasonable choice here. In most cases, it should choose correctly.

    Also keep in mind that you can't "pass logically by value, actually by reference" (which is what the compiler is doing) yourself; that would imply using &move reference, which doesn't exist in the language yet.

    I believe Steve's comment there should be interpreted a little more liberally as: "don't override the compiler yourself unless profiling tells you to."

  • So is there any difference between the two? Yes! If you pass a [u8; 0x2000], then the caller has to make a copy of the value before it passes it, assuming you want to be able to use it again later on. That's likely where the performance difference is coming from.

    Note that the compiler won't make a copy if you are moving the value into the function. This includes all non-copyable types (like Box, String and Vec), and should include copyable types that the compiler knows you aren't going to use again.

The appropriate choice is &[u8; 0x2000] because:

  • You only need to read from the value (i.e. if you wanted to modify it, you'd need a &mut [u8; 0x2000].
  • You don't want to take ownership of the value (in which case, you'd pass by value).

Again, to re-emphasize: don't start playing games with "this is a big value therefore I will use pass-by-reference" because the compiler is already doing this, and you it's really hard to get it right manually.

like image 112
DK. Avatar answered Oct 17 '22 10:10

DK.