8

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

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

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

4

2 回答 2

5

(如果你喜欢,你可以阅读最后的结论)

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

#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 毫秒

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

于 2014-06-18T12:32:09.437 回答
1

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

如果函数/构造函数是内联的,编译器可能会对其进行优化。如果函数不是内联的,则每次调用都会将值推入堆栈,因此会对性能产生影响(显着或不显着)。

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

于 2014-06-18T12:49:17.207 回答