-1

I want to test speed of the passing by value and passing by reference in C++:

class MyAddress{
    char *name;
    long int number;
    char *street;
    char *town;
    char state[2];
    long zip;
    std::vector<int> v_int;
public:
    MyAddress(int i){
        v_int.resize(1000000);
        std::fill(v_int.begin(),v_int.end(),i);
    }
    MyAddress& assign1(MyAddress const& x)
    { 
        MyAddress tmp(x);       // copy construction of tmp does the hard work
        std::swap(*this, tmp);  // trade our resources for tmp's
        return *this;           // our (old) resources get destroyed with tmp 
    }
    MyAddress& assign2(MyAddress x)//x is a copy of the source 
    {                              //hard work already done

        std::swap(*this, x);  // trade our resources for x's
        return *this;         // our (old) resources get destroyed with x 
    }
    void f1(MyAddress v){int i=v.v_int[3];}
    void f2(MyAddress const &ref){int i=ref.v_int[3];}

};

MyAddress get_names(MyAddress& ref){return ref;}

main:

int _tmain(int argc, _TCHAR* argv[])
{
    float time_elapsed1=0;
    float time_elapsed2=0;

    for(int i=0;i<100;i++){
        {
            MyAddress a1(1);
            MyAddress a2(2);
            MyAddress a3(3);
            clock_t tstart=std::clock();
            a1.f1(a2);
            a1.f1(a3);
            clock_t tend=std::clock();
            time_elapsed1+=((float)tend-(float)tstart);
        }
        {
            MyAddress a1(1);
            MyAddress a2(2);
            MyAddress a3(3);
            clock_t tstart=std::clock();
            a1.f2(a2);
            a1.f2(a3);
            clock_t tend=std::clock();
            time_elapsed2+=((float)tend-(float)tstart);
        }
    }
    std::cout<<std::fixed<<"\nassign1 time elapsed : "
         <<time_elapsed1/CLOCKS_PER_SEC;
    std::cout<<std::fixed<<"\nassign2 time elapsed : "
         <<time_elapsed2/CLOCKS_PER_SEC;
    system("pause");
    return 0;
}

The time difference result is shocking:

assign1 time elapsed : 81.044998

assign2 time elapsed : 0.002000

is this correct?

How can the speeds for "by value" be so much more than "by reference"?

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
4pie0
  • 29,204
  • 9
  • 82
  • 118
  • 1
    Why are you using `std::vector`, but not `std::string`? Now you're not following the rule of 3/5/6/whatever you want to call it. Anyways, passing by value can invoke an expensive copy constructor. – chris May 20 '12 at 17:09
  • 1
    Not really surprising. Copying 4MB vector has to take some time. – zch May 20 '12 at 17:11
  • You're not using `assign1` and `assign2` anywhere in your code, yet you've mentioned them in the output? – Nawaz May 20 '12 at 17:13
  • I actually did some sort of similar testing with this yesterday. It took me about 6 minutes to copy 1GB of memory 100 times by value, but 78ms by using move semantics. The by value can really add up. – chris May 20 '12 at 17:14
  • What @zch said. But still you might need a new machine - the slowest run I got (with a non-optimized compile) was around 6 seconds. – Michael Burr May 20 '12 at 17:16
  • @MichaelBurr, definitely. The first one keeps giving me just over 0.3 with no optimizations. – chris May 20 '12 at 17:20
  • @zch: yeah, what a pitty that new machines do not fall from heaven : D, thanks – 4pie0 May 20 '12 at 17:23
  • Too long unnecessary code snippet. – Lion May 20 '12 at 18:50
  • I'm quite shocked, that actually you're suprised by this, while being able to use swap somewhat properly, and not creating a total bullcrap c++ code. – Jitsu Feb 18 '15 at 10:17

2 Answers2

1

There is some confusion in the wordings of your ouput: you're not using assign1 and assign2 anywhere in your code, yet you've mentioned them in the output.

Anyway, after seeing what you're actually doing, all I can say that f1 takes the argument by value which means it calls the copy-constructor, which in turn copies the member vector which is too huge. This copying of the vector takes that much time, which you save when you call f2 as it takes the argument by reference, so no copy of the vector is made in this case.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
0

In addition to what has already been said about expensive copy constructors, I want to mention that you're just throwing away the results of the calculations in f1 and f2. That means that in theory (given that the vector is implemented in a way so that this would be possible) the compiler could decide to remove the entire method call when values are only read and it's obvious that removing them wouldn't cause side effects. That means, that you might end up only measuring the time it takes to start and stop the timer itself.

Regardless, you should expect that call by reference is quicker in all but a few cases, at least if you're passing a large object along.

Anders Sjöqvist
  • 3,372
  • 4
  • 21
  • 22
  • no, all in all in f2() the int is read from the vector, right? so I have comparison - it is what I wanted to see – 4pie0 May 20 '12 at 17:37
  • @cf16 Yes, you're reading from the vector in both cases, but you're throwing away the result, can we agree on that? You're returning `void`, and the variable `i` is never stored or passed on anywhere. This means that if the compiler is clever, it'll decide to skip the calculation altogether, since no one will ever hear about the result. This is what [compiler optimization](http://en.wikipedia.org/wiki/Compiler_optimization) techniques such as **straight line code**, **dead store elimination** and **dead code elimination** are all about (all of which are relevant in this case, check them out). – Anders Sjöqvist May 20 '12 at 18:06
  • but if compiler decide to skip, isn't it the case in both functions? both returns void right? this what you tell falls to both of them, but times differ – 4pie0 May 20 '12 at 18:37
  • please see the changes I have made. now functions return int. – 4pie0 May 20 '12 at 18:42
  • 1
    @cf16 The difference is that if the compiler can remove an entire function call, then much more could be affected. The creation of a class could potentially have side effects (such as printing "i'm the constructor" to stdout for example), and it's way more difficult to deal with that, so code causing that will likely remain. You're correct, the changes you made make optimizations less likely. Despite everything I've said, however, I should point out that the vector implementation of `operator[]` might in itself be a method too complicated to remove altogether. But it's hard to know for sure. – Anders Sjöqvist May 20 '12 at 18:53
  • 1
    @cf16 Also, don't think of my answers as an explanation for the difference in execution time in this particular situation. The allocation and initialization of new objects is certainly the cause here, and I personally use const references all the time. I'm just saying that in order to make a fair comparison, you should make sure that the different methods are actually doing exactly the same thing. You can often control the optimization level in the compiler, but comparing efficiency in non-optimized code might not be what you're looking to do either, since you'll only ship optimized code... – Anders Sjöqvist May 20 '12 at 18:59