4

I thought that pass by reference should be faster then pass by value because the computer isn't copying data, it just points to the address of data.

But, consider the following C++ code:

#include <iostream>
#include <cassert>
#include <cmath>

using namespace std;

// do not use pass by reference& with this function, it will become so slow
unsigned long long computePascal(const unsigned int row, const unsigned int position) {
    if (position == 1 || position == row)
    return 1L;
    unsigned long long left = computePascal(row-1, position-1);
    unsigned long long right = computePascal(row-1, position);
    unsigned long long sum = left + right;
    return sum;
}

int main() {
    int row, position;
    cout << "Input row and position: ";
    cin >> row >> position;
    assert(position > 0 && position <= row);
    unsigned long long pascalNumber = computePascal(row, position);
    cout << pascalNumber << endl;
    return 0;
}

Now this code is just a normal program that compute pascal number recursively by entering the desired row and position of the triangle.

I tried putting row 50 and position 7 and it computes around 1 second (pass by value). The output is about 13 million which is correct. So I thought that it could be faster if I pass by reference instead because it doesn't need to copy a lot of data. But this is very wrong and I don't know why it took longer time about 3 times the amount it took for passing by value. The question is ...

Why this program computes so slow after I try changing it to pass by const reference?

Does it matter that because recursive function is an exception that you should pass by value rather than const reference?

off99555
  • 3,797
  • 3
  • 37
  • 49
  • 6
    "isn't copying data, it just points to the address of data" - if the data is smaller than an address, then copying it might be faster (or at least no slower) than passing the address to the function. And passing by reference adds a level of indirection, which has a cost. – Mike Seymour Oct 24 '14 at 16:47
  • 1
    The type int* is at least as big as an int itself, so you're not saving any time on copying, and have to do a couple address of and dereference operations on top of it. – Sean Oct 24 '14 at 16:47
  • 4
    There is no clear-cut rule to which is faster. When integers are passed by value, they can be stored in registers, which are very fast. Usually you should pass basic data types by value for this reason. It is usually only worth it when the cost to copy the type is large. – Neil Kirk Oct 24 '14 at 16:47
  • 1
    Just saying: If your calculation takes more than a few microseconds, you are doing something very wrong. I'd say inappropriate use of recursion. If the result is n, then you are making n calls. – gnasher729 Oct 24 '14 at 16:49
  • It gives me almost the same time with my computer/compiler... To understand what happens with yours, try to observe the assembler code. It may construct some more variables when you pass expressions by reference, i.e. `row-1` need some variable to be passed by reference, which may take more time to compute. – Jean-Baptiste Yunès Oct 24 '14 at 16:52
  • 1
    Oh, I forget thinking that an address is larger than the data itself. Thank you for clarification. Just answer and I will mark you as best answer and close the thread. I got this idea from learncpp.com that you should always use pass by const reference rather than passing by value all the time because the data doesn't need to be cloned. So I think this is wrong now. But what is wrong with this question? Because I don't know so I got down-voted? Or the question is too easy for some other people? – off99555 Oct 24 '14 at 16:56

1 Answers1

19

The answer to "Which is faster" is usually "It depends".

If instead of passing four bytes of data you are passing an eight byte pointer to data, then you can't really expect that to make things faster. If instead of passing 100 bytes of data you are passing an eight byte pointer to data, that's different.

But now the function doesn't have the data, it only has a reference. So whenever it needs to read the data, it has to do that indirectly through the reference. That takes longer. If you pass a 100 byte object and only read eight byte of it, you still are likely to win. But if you actually read all the data, and maybe multiple times, then it could easily be faster to pass the value even for large objects.

The real difference comes when you pass an object, and passing by value means a more or less complex constructor will be called. Passing by reference means no constructor. But int has no constructor anyway.

And then there is optimisation. Passing by value means the compiler knows your function is the only one with access to the data. Pass by reference means the data could be anywhere. If you have two int& parameters, I could pass the some int twice. So increasing row might increase pos. Or it might not. That kills optimisations.

And then there is the rule of optimisation: "Measure it". You measured it and found what's faster. Sometimes things are faster or slower for no good reason whatsoever.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • Are passing by reference and passing by pointer the same in memory allocation aspect? – off99555 Oct 24 '14 at 18:31
  • No. Passing a reference will not increase the stack as much as passing everything by value (copy). – Entalpi Sep 16 '17 at 15:06
  • @off99555 Yes, if you follow exactly what passing by reference has to assume, i.e. referenced value might be changed inside the calling function so dereferencing of `row` to be subtracted by one has to be done the another time even though `row` itself is never changed. If we go by your function with the arguments converted into references, then it is identical to a function where the arguments are passed by pointers, plus definition of stack values with the arithmetics for which the addresses are passed to the recursive call. See https://godbolt.org/z/o5NRoI for example. – syockit Sep 23 '19 at 02:16
  • Isn't value usually slower since you can't take advantage of the cpu cache nearly as much for large objects? I thought performance usually involves going to memory as little as possible today. – ZeroPhase Jun 01 '21 at 09:30