Call by value vs. Call by reference

Discussion in 'C++' started by Saeed Amrollahi, Jul 11, 2008.

  1. Dear All
    Hi

    I have learned when an object is big, I should pass the object using a
    reference or pointer to it rather than calling by value.
    In the following code, I don't gain cosiderable performance with call-
    by-reference vs. call-by-value. Indeed, the perforamnce is absolutely
    unconsiderable:

    #include <vector>
    #include <iostream>

    using namespace std;
    void f1(std::vector<int> v)
    {
    cout << "f1 called" << '\n';
    for (std::vector<int>::size_type sz = 0; sz < v.size(); sz++)
    v[sz] = sz;
    cout << "f1 end" << '\n';
    }

    void f2(std::vector<int> v)
    {
    cout << "f2 called" << '\n';
    for (std::vector<int>::size_type sz = 0; sz < v.size(); sz++)
    v[sz] = sz;
    cout << "f2 end" << '\n';
    }

    void f3(std::vector<int>* pv)
    {
    cout << "f3 called" << '\n';
    for (std::vector<int>::size_type sz = 0; sz < pv->size(); sz++)
    (*pv)[sz] = sz;
    cout << "f3 end" << '\n';
    }
    int main()
    {
    vector<int> v(50000000);
    f1(v);
    f2(v);
    f3(&v);

    return 0;
    }

    I ran under Visual Studio 2005, Visual Studio 2008 and GCC. Is it
    about some compiler switches that I should on/off? Did compilers
    become more clever?

    In advance, thank you for your comments.
    - Saeed Amrollahi
    Saeed Amrollahi, Jul 11, 2008
    #1
    1. Advertising

  2. Saeed Amrollahi

    Ron AF Greve Guest

    Hi,


    I would expect you see a large difference since if you pass by value it has
    to copy the whole vecor, however..

    In your code the compiler might have removed the whole vector copying during
    optimization since you don't do anything with it. You wouldn't notice a
    difference between a program with the vector passed in and one completely
    without and without assignment

    Regards, Ron AF Greve

    http://www.InformationSuperHighway.eu

    "Saeed Amrollahi" <> wrote in message
    news:...
    > Dear All
    > Hi
    >
    > I have learned when an object is big, I should pass the object using a
    > reference or pointer to it rather than calling by value.
    > In the following code, I don't gain cosiderable performance with call-
    > by-reference vs. call-by-value. Indeed, the perforamnce is absolutely
    > unconsiderable:
    >
    > #include <vector>
    > #include <iostream>
    >
    > using namespace std;
    > void f1(std::vector<int> v)
    > {
    > cout << "f1 called" << '\n';
    > for (std::vector<int>::size_type sz = 0; sz < v.size(); sz++)
    > v[sz] = sz;
    > cout << "f1 end" << '\n';
    > }
    >
    > void f2(std::vector<int> v)
    > {
    > cout << "f2 called" << '\n';
    > for (std::vector<int>::size_type sz = 0; sz < v.size(); sz++)
    > v[sz] = sz;
    > cout << "f2 end" << '\n';
    > }
    >
    > void f3(std::vector<int>* pv)
    > {
    > cout << "f3 called" << '\n';
    > for (std::vector<int>::size_type sz = 0; sz < pv->size(); sz++)
    > (*pv)[sz] = sz;
    > cout << "f3 end" << '\n';
    > }
    > int main()
    > {
    > vector<int> v(50000000);
    > f1(v);
    > f2(v);
    > f3(&v);
    >
    > return 0;
    > }
    >
    > I ran under Visual Studio 2005, Visual Studio 2008 and GCC. Is it
    > about some compiler switches that I should on/off? Did compilers
    > become more clever?
    >
    > In advance, thank you for your comments.
    > - Saeed Amrollahi
    Ron AF Greve, Jul 11, 2008
    #2
    1. Advertising

  3. Saeed Amrollahi

    Soumen Guest

    On Jul 11, 1:37 pm, Saeed Amrollahi <> wrote:
    > Dear All
    > Hi
    >
    > I have learned when an object is big, I should pass the object using a
    > reference or pointer to it rather than calling by value.
    > In the following code, I don't gain cosiderable performance with call-
    > by-reference vs. call-by-value. Indeed, the perforamnce is absolutely
    > unconsiderable:
    >
    > #include <vector>
    > #include <iostream>
    >
    > using namespace std;
    > void f1(std::vector<int> v)
    > {
    >         cout << "f1 called" << '\n';
    >         for (std::vector<int>::size_type sz = 0; sz < v.size(); sz++)
    >                 v[sz] = sz;
    >         cout << "f1 end" << '\n';
    >
    > }
    >
    > void f2(std::vector<int> v)
    > {
    >         cout << "f2 called" << '\n';
    >         for (std::vector<int>::size_type sz = 0; sz < v.size(); sz++)
    >                 v[sz] = sz;
    >         cout << "f2 end" << '\n';
    >
    > }
    >
    > void f3(std::vector<int>* pv)
    > {
    >         cout << "f3 called" << '\n';
    >         for (std::vector<int>::size_type sz = 0; sz < pv->size(); sz++)
    >                 (*pv)[sz] = sz;
    >         cout << "f3 end" << '\n';}
    >
    > int main()
    > {
    >         vector<int> v(50000000);
    >         f1(v);
    >         f2(v);
    >                 f3(&v);
    >
    >         return 0;
    >
    > }
    >
    > I ran under Visual Studio 2005, Visual Studio 2008 and GCC. Is it
    > about some compiler switches that I should on/off? Did compilers
    > become more clever?
    >
    > In advance, thank you for your comments.
    >  - Saeed Amrollahi


    Probably because your vector is empty (may be it's just holding a
    pointer and few other int)and you're inserting elements inside the
    function.
    Try inserting elements before calling the function and call the
    function 1000 times or so. Also try vector of string and let it have
    all same
    elements. Let me know what you find.

    Regards,
    ~ Soumen
    Soumen, Jul 11, 2008
    #3
  4. Saeed Amrollahi wrote:
    > I have learned when an object is big, I should pass the object using a
    > reference or pointer to it rather than calling by value.
    > In the following code, I don't gain cosiderable performance with call-
    > by-reference vs. call-by-value. Indeed, the perforamnce is absolutely
    > unconsiderable:


    How do you measure the speed of the program? Maybe copying that vector
    is relatively so fast that you don't notice the difference. Try calling
    each function in a loop to see if there's a difference.
    Juha Nieminen, Jul 11, 2008
    #4
  5. Saeed Amrollahi

    Guest

    On Jul 11, 7:08 am, Soumen <> wrote:
    > On Jul 11, 1:37 pm, Saeed Amrollahi <> wrote:


    > > void f1(std::vector<int> v)
    > > {
    > >         cout << "f1 called" << '\n';
    > >         for (std::vector<int>::size_type sz = 0; sz < v.size(); sz++)
    > >                 v[sz] = sz;


    That is not "inserting" an element, rather assigning to an existing
    one.

    > >         cout << "f1 end" << '\n';

    >
    > > }


    > >         vector<int> v(50000000);


    That is a vector with 50 million ints, not empty.

    > Probably because your vector is empty (may be it's just holding a
    > pointer and few other int)and you're inserting elements inside the
    > function.


    That would be this code:

    vector<int> v; // empty
    /* ... */
    v.push_back(sz); // insert

    Ali
    , Jul 11, 2008
    #5
  6. Saeed Amrollahi schrieb:
    > Dear All
    > Hi
    >
    > I have learned when an object is big, I should pass the object using a
    > reference or pointer to it rather than calling by value.
    > In the following code, I don't gain cosiderable performance with call-
    > by-reference vs. call-by-value. Indeed, the perforamnce is absolutely
    > unconsiderable:
    >

    [...]
    > void f1(std::vector<int> v)

    [...]
    > void f2(std::vector<int> v)

    [...]
    > void f3(std::vector<int>* pv)

    [...]

    There is no reference. You have two function that do a call-by-value and
    one that does a call-by-reference (via pointer).

    Obviously, the first two shouldn't show any measurable performance
    difference.

    --
    Thomas
    Thomas J. Gritzan, Jul 11, 2008
    #6
  7. On Jul 11, 9:54 pm, "Thomas J. Gritzan" <>
    wrote:
    > Saeed Amrollahi schrieb:> Dear All
    > > Hi

    >
    > > I have learned when an object is big, I should pass the object using a
    > > reference or pointer to it rather than calling by value.
    > > In the following code, I don't gain cosiderable performance with call-
    > > by-reference vs. call-by-value. Indeed, the perforamnce is absolutely
    > > unconsiderable:

    >
    > [...]
    > > void f1(std::vector<int> v)

    > [...]
    > > void f2(std::vector<int> v)

    > [...]
    > > void f3(std::vector<int>* pv)

    >
    > [...]
    >
    > There is no reference. You have two function that do a call-by-value and
    > one that does a call-by-reference (via pointer).
    >
    > Obviously, the first two shouldn't show any measurable performance
    > difference.
    >
    > --
    > Thomas


    Hi
    Sorry for typo and confusion. I actually used reference in the code:
    void f1(std::vector<int> v)
    void f2(std::vector<int>& v)
    void f3(std::vector<int>* pv)

    Based on
    Saeed Amrollahi, Jul 11, 2008
    #7
  8. On Jul 11, 9:54 pm, "Thomas J. Gritzan" <>
    wrote:
    > Saeed Amrollahi schrieb:> Dear All
    > > Hi

    >
    > > I have learned when an object is big, I should pass the object using a
    > > reference or pointer to it rather than calling by value.
    > > In the following code, I don't gain cosiderable performance with call-
    > > by-reference vs. call-by-value. Indeed, the perforamnce is absolutely
    > > unconsiderable:

    >
    > [...]
    > > void f1(std::vector<int> v)

    > [...]
    > > void f2(std::vector<int> v)

    > [...]
    > > void f3(std::vector<int>* pv)

    >
    > [...]
    >
    > There is no reference. You have two function that do a call-by-value and
    > one that does a call-by-reference (via pointer).
    >
    > Obviously, the first two shouldn't show any measurable performance
    > difference.
    >
    > --
    > Thomas



    Hi
    Sorry for typo and confusion. I actually used reference in the code:
    void f1(std::vector<int> v)
    void f2(std::vector<int>& v)
    void f3(std::vector<int>* pv)

    Based on guys' advice, I changed my simple benckmark code to better
    one:

    template<class T>
    void f1(std::vector<T> v)
    {
    for (int sz = 0; sz < 1000000; sz++)
    v.push_back(T());
    }

    template<class T>
    void f2(std::vector<T>& v)
    {
    for (int sz = 0; sz < 1000000; sz++)
    v.push_back(T());
    }

    template<class T>
    void f3(std::vector<T>* pv)
    {
    for (int sz = 0; sz < 1000000; sz++)
    pv->push_back(T());

    }

    int main()
    {

    vector<int> v; // empty vector

    cout << "f1 called" << '\n';
    for (int i = 0; i < 10; i++) {
    vector<int> v;
    f1(v);
    }
    cout << "f1 end" << '\n';
    v.clear();
    cout << "f2 called" << '\n';
    for (int i = 0; i < 10; i++) {
    vector<int> v;
    f2(v);
    }
    cout << "f2 end" << '\n';
    v.clear();
    cout << "f3 called" << '\n';
    for (int i = 0; i < 10; i++) {
    vector<int> v;
    f3(&v);
    }
    cout << "f3 end" << '\n';
    v.clear();

    return 0;
    }

    I don't use a precise clock, but I expected a difference order of
    magnitude efficiency but it is not.
    I tested with vector<int>, vector<double> and vector<string>.


    Regards,
    Saeed
    Saeed Amrollahi, Jul 11, 2008
    #8
  9. On 2008-07-11 21:44, Saeed Amrollahi wrote:
    > On Jul 11, 9:54 pm, "Thomas J. Gritzan" <>
    > wrote:
    >> Saeed Amrollahi schrieb:> Dear All
    >> > Hi

    >>
    >> > I have learned when an object is big, I should pass the object using a
    >> > reference or pointer to it rather than calling by value.
    >> > In the following code, I don't gain cosiderable performance with call-
    >> > by-reference vs. call-by-value. Indeed, the perforamnce is absolutely
    >> > unconsiderable:

    >>
    >> [...]
    >> > void f1(std::vector<int> v)

    >> [...]
    >> > void f2(std::vector<int> v)

    >> [...]
    >> > void f3(std::vector<int>* pv)

    >>
    >> [...]
    >>
    >> There is no reference. You have two function that do a call-by-value and
    >> one that does a call-by-reference (via pointer).
    >>
    >> Obviously, the first two shouldn't show any measurable performance
    >> difference.
    >>
    >> --
    >> Thomas

    >
    >
    > Hi
    > Sorry for typo and confusion. I actually used reference in the code:
    > void f1(std::vector<int> v)
    > void f2(std::vector<int>& v)
    > void f3(std::vector<int>* pv)
    >
    > Based on guys' advice, I changed my simple benckmark code to better
    > one:
    >
    > template<class T>
    > void f1(std::vector<T> v)
    > {
    > for (int sz = 0; sz < 1000000; sz++)
    > v.push_back(T());
    > }
    >
    > template<class T>
    > void f2(std::vector<T>& v)
    > {
    > for (int sz = 0; sz < 1000000; sz++)
    > v.push_back(T());
    > }
    >
    > template<class T>
    > void f3(std::vector<T>* pv)
    > {
    > for (int sz = 0; sz < 1000000; sz++)
    > pv->push_back(T());
    >
    > }
    >
    > int main()
    > {
    >
    > vector<int> v; // empty vector
    >
    > cout << "f1 called" << '\n';
    > for (int i = 0; i < 10; i++) {
    > vector<int> v;
    > f1(v);
    > }
    > cout << "f1 end" << '\n';
    > v.clear();
    > cout << "f2 called" << '\n';
    > for (int i = 0; i < 10; i++) {
    > vector<int> v;
    > f2(v);
    > }
    > cout << "f2 end" << '\n';
    > v.clear();
    > cout << "f3 called" << '\n';
    > for (int i = 0; i < 10; i++) {
    > vector<int> v;
    > f3(&v);
    > }
    > cout << "f3 end" << '\n';
    > v.clear();
    >
    > return 0;
    > }
    >
    > I don't use a precise clock, but I expected a difference order of
    > magnitude efficiency but it is not.
    > I tested with vector<int>, vector<double> and vector<string>.


    Make sure you measure the right thing:

    #include <iostream>
    #include <vector>

    size_t f1(std::vector<int> v)
    {
    return v.size();
    }

    size_t f2(const std::vector<int>& v)
    {
    return v.size();
    }

    size_t f3(std::vector<int>* v)
    {
    return v->size();
    }

    int main()
    {

    std::vector<int> v(1000000); // empty vector
    std::cout << "Start" << std::endl;

    for (int i = 0; i < 1000; ++i)
    f1(v);
    std::cout << "f1 done" << std::endl;

    for (int i = 0; i < 1000; ++i)
    f2(v);
    std::cout << "f2 done" << std::endl;

    for (int i = 0; i < 1000; ++i)
    f3(&v);
    std::cout << "f3 done" << std::endl;

    return 0;
    }

    This shows the overhead of passing a vector with 10^6 elements by value
    instead of by pointer/reference. If you want to be sure you can
    rearrange the calls so that f1() is called in second or third and see
    what takes the longest.

    --
    Erik Wikström
    Erik Wikström, Jul 11, 2008
    #9
  10. On Jul 11, 11:06 pm, Erik Wikström <> wrote:
    > On 2008-07-11 21:44, Saeed Amrollahi wrote:
    >
    >
    >
    >
    >
    > > On Jul 11, 9:54 pm, "Thomas J. Gritzan" <>
    > > wrote:
    > >> Saeed Amrollahi schrieb:> Dear All
    > >> > Hi

    >
    > >> > I have learned when an object is big, I should pass the object using a
    > >> > reference or pointer to it rather than calling by value.
    > >> > In the following code, I don't gain cosiderable performance with call-
    > >> > by-reference vs. call-by-value. Indeed, the perforamnce is absolutely
    > >> > unconsiderable:

    >
    > >> [...]
    > >> > void f1(std::vector<int> v)
    > >> [...]
    > >> > void f2(std::vector<int> v)
    > >> [...]
    > >> > void f3(std::vector<int>* pv)

    >
    > >> [...]

    >
    > >> There is no reference. You have two function that do a call-by-value and
    > >> one that does a call-by-reference (via pointer).

    >
    > >> Obviously, the first two shouldn't show any measurable performance
    > >> difference.

    >
    > >> --
    > >> Thomas

    >
    > > Hi
    > > Sorry for typo and confusion. I actually used reference in the code:
    > > void f1(std::vector<int> v)
    > > void f2(std::vector<int>& v)
    > > void f3(std::vector<int>* pv)

    >
    > > Based on guys' advice, I changed my simple benckmark code to better
    > > one:

    >
    > > template<class T>
    > > void f1(std::vector<T> v)
    > > {
    > >    for (int sz = 0; sz < 1000000; sz++)
    > >            v.push_back(T());
    > > }

    >
    > > template<class T>
    > > void f2(std::vector<T>& v)
    > > {
    > >    for (int sz = 0; sz < 1000000; sz++)
    > >            v.push_back(T());
    > > }

    >
    > > template<class T>
    > > void f3(std::vector<T>* pv)
    > > {
    > >    for (int sz = 0; sz < 1000000; sz++)
    > >            pv->push_back(T());

    >
    > > }

    >
    > > int main()
    > > {

    >
    > >    vector<int> v; // empty vector

    >
    > >    cout << "f1 called" << '\n';
    > >    for (int i = 0; i < 10; i++) {
    > >            vector<int> v;
    > >            f1(v);
    > >    }
    > >    cout << "f1 end" << '\n';
    > >    v.clear();
    > >    cout << "f2 called" << '\n';
    > >    for (int i = 0; i < 10; i++) {
    > >            vector<int> v;
    > >            f2(v);
    > >    }
    > >    cout << "f2 end" << '\n';
    > >    v.clear();
    > >    cout << "f3 called" << '\n';
    > >    for (int i = 0; i < 10; i++) {
    > >            vector<int> v;
    > >            f3(&v);
    > >    }
    > >    cout << "f3 end" << '\n';
    > >         v.clear();

    >
    > >    return 0;
    > > }

    >
    > > I don't use a precise clock, but I expected a difference order of
    > > magnitude efficiency but it is not.
    > > I tested with vector<int>, vector<double> and vector<string>.

    >
    > Make sure you measure the right thing:
    >
    > #include <iostream>
    > #include <vector>
    >
    > size_t f1(std::vector<int> v)
    > {
    >     return v.size();
    >
    > }
    >
    > size_t f2(const std::vector<int>& v)
    > {
    >     return v.size();
    >
    > }
    >
    > size_t f3(std::vector<int>* v)
    > {
    >     return v->size();
    >
    > }
    >
    > int main()
    > {
    >
    >     std::vector<int> v(1000000); // empty vector
    >     std::cout << "Start" << std::endl;
    >
    >     for (int i = 0; i < 1000; ++i)
    >         f1(v);
    >     std::cout << "f1 done" << std::endl;
    >
    >     for (int i = 0; i < 1000; ++i)
    >         f2(v);
    >     std::cout << "f2 done" << std::endl;
    >
    >     for (int i = 0; i < 1000; ++i)
    >         f3(&v);
    >     std::cout << "f3 done" << std::endl;
    >
    >     return 0;
    >
    > }
    >
    > This shows the overhead of passing a vector with 10^6 elements by value
    > instead of by pointer/reference. If you want to be sure you can
    > rearrange the calls so that f1() is called in second or third and see
    > what takes the longest.
    >
    > --
    > Erik Wikström- Hide quoted text -
    >
    > - Show quoted text -


    Hi
    I ran your program and I do see the big difference. I really don't
    know why my benchmark doesn't work.
    I should review my code. Erik, Thomas, Ali, Juha, Soumen and Ron thank
    you.

    ---
    Saeed Amrollahi
    Saeed Amrollahi, Jul 12, 2008
    #10
  11. Saeed Amrollahi

    KalleGuld Guest


    >
    >         cout << "f1 called" << '\n';
    >         for (int i = 0; i < 10; i++) {
    >                 vector<int> v;
    >                 f1(v);
    >         }
    >         cout << "f1 end" << '\n';
    >         v.clear();
    >         cout << "f2 called" << '\n';
    >         for (int i = 0; i < 10; i++) {
    >                 vector<int> v;
    >                 f2(v);
    >         }
    >         cout << "f2 end" << '\n';
    >         v.clear();
    >         cout << "f3 called" << '\n';
    >         for (int i = 0; i < 10; i++) {
    >                 vector<int> v;
    >                 f3(&v);
    >         }


    The problem with your benchmark is that most of the work is done
    within the functions themselves.

    > I have learned when an object is big, I should pass the object using a
    > reference or pointer to it rather than calling by value.


    That's true, but you must understand why. The reason is that when
    calling by value, you make a copy of the object, and copying that
    object takes time.
    But in your example, you just pass an empty vector, and that's quite
    quick to copy.
    Furthermore, it's only _starting_ the function that suffers when
    calling by value, not the work done _inside_ it.

    So, to make your benchmark work better, strip the body of your
    functions and call your functions more times.
    The code below should show the difference quite clearly (part 1 took
    about 3 minutes, and part 2 was under a second on my system).


    //speedtest.cpp
    #include <vector>

    #include <iostream>
    using namespace std;


    double f1(vector<double> in) {
    return in[0];
    }
    double f2(vector<double> &in) {
    return in[1];
    }
    int main() {

    vector<double> vec(1000000); //big vector
    vec[0]=1.2;
    vec[1]=3.4;
    double db;
    int bigNum = 10000;
    cout << "start f1:\n" << endl; //remember to flush...
    for (int i=0;i< bigNum ;i++) {
    db = f1(vec);
    }
    cout << "start f2:\n" << endl;
    for (int i=0;i< bigNum ;i++) {
    db = f2(vec);
    }
    cout << "end" << endl;

    return 0;
    }
    KalleGuld, Jul 15, 2008
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. sam pal
    Replies:
    3
    Views:
    526
    E. Robert Tisdale
    Jul 16, 2003
  2. Alexander Stippler

    call by value / (const) reference

    Alexander Stippler, Jan 25, 2004, in forum: C++
    Replies:
    9
    Views:
    1,702
    E. Robert Tisdale
    Jan 26, 2004
  3. Replies:
    13
    Views:
    806
    Chris Dollin
    Feb 6, 2006
  4. ravi
    Replies:
    10
    Views:
    1,139
    Chris Torek
    Feb 23, 2007
  5. Nehil

    C : Call by value or reference

    Nehil, Jul 15, 2007, in forum: C Programming
    Replies:
    24
    Views:
    854
    Chris Dollin
    Jul 16, 2007
Loading...

Share This Page