未使用的默认参数会降低性能c ++

时间:2014-06-18 12:32:09

标签: c++ performance function arguments default-arguments

假设我声明了一个函数foo(int arg1, int arg2 = 0, int arg3 = 0, int arg4 = 0)。最后三个参数将仅偶尔指定(如果有的话),并且大多数情况下,函数将被称为foo(some_int)。我会通过将函数声明为foo(int arg1)来获得性能,并且如果确实需要其他参数,则可以使用不同的解决方案吗?

换句话说,声明但未指定的默认参数会使函数调用变慢?

在这种情况下,函数是一个对象的构造函数,但它是一个普遍的问题。

2 个答案:

答案 0 :(得分:3)

(如果你愿意,你可以在最后阅读结论)

我做了一个基准来测试这个,我第一次运行这个短程序十次:

#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;
}

基本上执行函数returnMe一亿次,然后告诉我需要多长时间。值范围从280毫秒到318毫秒。然后我运行了这个程序:

#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;
}

大约十次,现在的值从584毫秒到624毫秒不等。

结论:是的,它会使函数调用变慢,但只需要很少量。创建一个单独的函数来将其他参数传递给对象,或者使用不同的构造函数,性能提升,但值得额外的代码吗?

还有另一种解决方法,由Box2D使用,它基本上为默认参数创建一个单独的结构,并将指针传递给它的实例。这样,当不需要设置额外的参数时,唯一的“垃圾参数”通过会降低你的性能是一个空指针,这并不是那么糟糕。如果要指定某些默认值,则在堆栈中创建所述结构的实例,填写所需的值,然后将其地址传递给该函数。简单,优雅,高效。

但是:两种提出的保存性能的解决方案(额外的功能和传递结构指针)都需要额外的代码。如果你的函数很少被调用,并且额外的参数不是那么多,那么保存的性能可能根本没有任何区别,如果是这样的话,那就不值得花时间了。 仅在必要时进行优化。请记住,我添加了12个默认参数,甚至没有将函数调用时间加倍。

====== 编辑:严肃测试的奖金。

所以前两个测试是用简单的简单编译命令g++ test.cpp -o test.exe完成的。正如许多评论中所指出的那样,这意味着-O0的优化级别。我们在-O3测试得到的结果是什么?

我重复了现在用g++ test.cpp -o test.exe -O3编译的测试,但发现程序现在在1-2毫秒内完成。我试图将迭代加速到一万亿,然后是一百万亿,同样的结果。所以我认为g ++可能看到我声明了一个我不会使用的变量,因此可能会跳过对returnMe的调用,也许完全是整个循环。

为了获得一些有用的结果,我向returnMe添加了实际功能,以确保它没有被优化掉。以下是使用的程序:

#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;
}

#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;
}

结果:

第一个程序:从653到686毫秒

第二个程序:从652到735毫秒

正如我所预料的那样,第二个程序仍然比第一个程序慢,但差异现在不太明显。

答案 1 :(得分:1)

这取决于您的编译器,启用的优化以及函数是否内联。

如果函数/构造函数是内联的,编译器可以优化它。如果函数不是内联函数,则每次调用都会将值推送到堆栈中,因此会对性能产生影响(重要或不重要)。

但请记住,过早优化是万恶之源。不要只是假设在运行配置文件之前编写一个不太可维护的代码来解决它并确保它需要优化。