Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will unused default arguments decrease performance c++

Suppose I declare a function foo(int arg1, int arg2 = 0, int arg3 = 0, int arg4 = 0). The last three arguments will be specified only occasionally (if ever), and mostly, the funciton will be called as foo(some_int). Would I gain performance by instead declaring the function as foo(int arg1), and having a different solution for passing the other arguments if they are really needed?

In other words, do declared but unspecified default arguments make a function call slower?

The function in this case is a constructor for an object, but it's a general question.

like image 430
Ludwik Avatar asked Jun 18 '14 12:06

Ludwik


People also ask

What are default arguments give the advantages of using default arguments?

A default argument is a value provided in a function declaration that is automatically assigned by the compiler if the calling function doesn't provide a value for the argument. In case any value is passed, the default value is overridden.

In what situations default arguments are useful?

Default arguments are useful in situations where some arguments always have the same value.

Does C default value?

There are no default parameters in C. One way you can get by this is to pass in NULL pointers and then set the values to the default if NULL is passed.

Can we use default arguments in C?

Default arguments are only allowed in the parameter lists of function declarations and lambda-expressions, (since C++11) and are not allowed in the declarations of pointers to functions, references to functions, or in typedef declarations.


2 Answers

(You can just read the conclusion at the end if you like)

I did a benchmark to test this, I first ran this short program about ten times:

#include <iostream>
#include <ctime>

using namespace std;

int returnMe(int me)
{
    return me;
}


int main()
{
   float begin = (float)clock();
   for(int i = 0; i < 100000000; i++)
   {
       int me = returnMe(i);
   }
   printf("\nTime: %f\n", begin);
   printf("\nTime: %f\n", (float)clock());

   return 0;
}

Which basically executes the function returnMe a hundred million times, and then tells me how long that took. Values ranged from 280 ms to 318ms. Then I ran this program:

#include <iostream>
#include <ctime>

using namespace std;

int returnMe(int me, int me87 = 0, int m8e = 0, int m5e = 0, int m34e = 0,int m1e = 0,int me234 = 0,int me332 = 0,int me43 = 0,int me34 = 0,int me3 = 0,int me2 = 0,int me1 = 0)
{
    return me;
}


int main()
{
   float begin = (float)clock();
   for(int i = 0; i < 100000000; i++)
   {
       int me = returnMe(i);
   }
   printf("\nTime: %f\n", begin);
   printf("\nTime: %f\n", (float)clock());

   return 0;
}

about ten times, and values were now ranging from 584 ms to 624 ms.

Conclusion: Yes, it will make the function call slower, but by a very small amount. Creating a separate function for passing the other arguments to the object, or having a different constructor, would be a performance gain, but would it be worth the extra code?

There is another way of solving it, used by Box2D, which is basically creating a separate struct for the default arguments, and passing a pointer to an instance of it. That way, when no extra arguments need to be set, the only "garbage argument" passed that decreases your performance is one nullpointer, and that is not so bad. When you want to specify some of the default values, you create an instance of said struct at stack, fill in the the values you want, then pass its address to the function. Easy, elegant and efficient.

However: Both proposed solutions for saving the performance (an extra function and passing a struct pointer) do require additional code. If your function will be called rarely, and the extra arguments are not that many, chances are the saved performance will not make any difference at all, and if that is the case, it's not worth your time. Only optimize if it's necessary. Remember I added 12 default arguments and didn't even double the function calling time.

======== EDIT: bonus for serious testing.

So the first two tests were done with plain simple compile command g++ test.cpp -o test.exe. As pointed out in numerous comments, that implies an optimization level of -O0. What results would we get from testing at -O3?

I repeated the tests now compiling with g++ test.cpp -o test.exe -O3, but found that the program was now completing in under 1-2 ms. I tried to vamp up the iterations to one trillion, then one hundred trillion, same result. So I figured g++ was probably seeing I was declaring a variable I was not going to use, and therefore probably skipping the calls to returnMe, and maybe the whole loop altogether.

To get some useful results, I added actual functionality to returnMe, to make sure that it was not optimized away. Here are the programs used:

#include <iostream>
#include <ctime>

using namespace std;

long long signed int bar = 0;

int returnMe(int me)
{
    bar -= me;
    return me;
}


int main()
{
   float begin = (float)clock();
   for(int i = 0; i < 1000000000; i++)
   {
       int me = returnMe(i);
       bar -= me * 2;
   }
   printf("\nTime: %f\n", begin);
   printf("\nTime: %f\n", (float)clock());
   printf("Bar: %i\n", bar);

   return 0;
}

and

#include <iostream>
#include <ctime>

using namespace std;

long long signed int bar = 0;

int returnMe(int me, int me87 = 0, int m8e = 0, int m5e = 0, int m34e = 0,int m1e = 0,int me234 = 0,int me332 = 0,int me43 = 0,int me34 = 0,int me3 = 0,int me2 = 0,int me1 = 0)
{
    bar -= me;
    return me;
}

int main()
{
   float begin = (float)clock();
   for(int i = 0; i < 1000000000; i++)
   {
       int me = returnMe(i);
       bar -= me * 2;
   }
   printf("\nTime: %f\n", begin);
   printf("\nTime: %f\n", (float)clock());
   printf("Bar: %i\n", bar);

   return 0;
}

Results:

First program: from 653 to 686 ms

Second program: from 652 to 735 ms

As I expected, the second program is still slower than the first, but the difference is now less noticeable.

like image 149
Ludwik Avatar answered Oct 01 '22 09:10

Ludwik


It will depend on your compiler, the optimizations enabled and whether or not the function is inline.

If the function/constructor is inline, the compiler may optimize that out. If the function is not inline, it will have the values pushed to the stack every call, so it will have performance impact (significant or not).

But remember, premature optimization is the root of all evil. Do not just assume it will be a big deal and writing a less maintainable code to go around it before you run a profile and be sure that it need optimization.

like image 33
fbafelipe Avatar answered Oct 01 '22 09:10

fbafelipe