C++ 未使用的默认参数是否会降低性能c++;
假设我声明了一个函数C++ 未使用的默认参数是否会降低性能c++;,c++,performance,function,arguments,default-arguments,C++,Performance,Function,Arguments,Default Arguments,假设我声明了一个函数foo(intarg1,intarg2=0,intarg3=0,intarg4=0)。最后三个参数只会偶尔指定(如果有),并且大多数情况下,该函数将被称为foo(some_int)。如果我将函数声明为foo(int arg1),并使用不同的解决方案来传递其他参数(如果确实需要的话),我会获得性能吗 换句话说,声明但未指定的默认参数是否会使函数调用变慢? 本例中的函数是对象的构造函数,但这是一个一般性问题。(如果您愿意,您可以在结尾处阅读结论) 我做了一个基准测试来测试这个,我
foo(intarg1,intarg2=0,intarg3=0,intarg4=0)
。最后三个参数只会偶尔指定(如果有),并且大多数情况下,该函数将被称为foo(some_int)
。如果我将函数声明为foo(int arg1)
,并使用不同的解决方案来传递其他参数(如果确实需要的话),我会获得性能吗
换句话说,声明但未指定的默认参数是否会使函数调用变慢?
本例中的函数是对象的构造函数,但这是一个一般性问题。(如果您愿意,您可以在结尾处阅读结论)
我做了一个基准测试来测试这个,我首先运行了这个简短的程序大约十次:
#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;
}
大约十次,现在的数值在584毫秒到624毫秒之间
结论:是的,这将使函数调用变慢,但速度非常小。创建一个单独的函数,将其他参数传递给对象,或者使用不同的构造函数,这将提高性能,但是否值得额外编写代码
Box2D使用另一种解决方法,基本上是为默认参数创建一个单独的结构,并将指针传递给它的实例。这样,当不需要设置额外的参数时,传递的唯一降低性能的“垃圾参数”是一个nullpointer,这并不是那么糟糕。当您想要指定一些默认值时,您可以在堆栈中创建所述结构的实例,填写所需的值,然后将其地址传递给函数。简单、优雅、高效
但是:两种建议的节省性能的解决方案(一个额外的函数和传递一个结构指针)都需要额外的代码。如果您的函数很少被调用,并且额外的参数也不多,那么保存的性能很可能根本不会有任何区别,如果是这样,那么就不值得您花费时间仅在必要时进行优化。还记得我添加了12个默认参数,甚至没有将函数调用时间增加一倍。
========
编辑:严重测试的奖励
因此,前两个测试是使用简单的编译命令g++test.cpp-o test.exe
完成的。正如许多评论中指出的,这意味着优化级别为-O0。我们在-O3下测试会得到什么结果
我重复了现在使用g++test.cpp-o test.exe-O3编译的测试,但发现程序现在在1-2毫秒内完成。我尝试将迭代次数增加到1万亿次,然后是100万亿次,结果相同。所以我想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;
}
#包括
#包括
使用名称空间std;
长符号整型条=0;
int returnMe(int me)
{
酒吧-=我;
还我;
}
int main()
{
浮点开始=(浮点)时钟();
对于(int i=0;i<100000000;i++)
{
int me=returnMe(i);
bar-=me*2;
}
printf(“\n时间:%f\n”,开始);
printf(“\n时间:%f\n”,(浮点)时钟();
printf(“条:%i\n”,条);
返回0;
}
及
#包括
#包括
使用名称空间std;
长符号整型条=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)
{
酒吧-=我;
还我;
}
int main()
{
浮点开始=(浮点)时钟();
对于(int i=0;i<100000000;i++)
{
int me=returnMe(i);
bar-=me*2;
}
printf(“\n时间:%f\n”,开始);
printf(“\n时间:%f\n”,(浮点)时钟();
printf(“条:%i\n”,条);
返回0;
}
结果:
第一个程序:从653毫秒到686毫秒
第二个程序:从652毫秒到735毫秒
正如我所料,第二个程序仍然比第一个程序慢,但差别现在不那么明显。这将取决于您的编译器、启用的优化以及函数是否是内联的
如果函数/构造函数是内联的,编译器可能会对此进行优化。如果函数不是内联的,则每次调用都会将值推送到堆栈中,因此会对性能产生影响(无论是否显著)
但请记住,过早优化是万恶之源。在运行概要文件之前,不要以为这会是一件大事,并编写一个不太容易维护的代码来绕过它,确保它需要优化。这是什么优化级别和编译器?@Ludwik您应该使用-O3或至少-O2进行测试。切勿在未启用优化的情况下测试性能,因为您不应在未启用优化的情况下发布,因此在未启用优化的情况下测试性能是毫无意义的。@Ludwik,任何现代优化编译器都将消除您对returnMe()
的调用,甚至取消for
循环本身的调用。这个基准测试你做错了。也许你有什么东西在运行,这会减慢第二个程序的执行。对于基准测试,你应该选择所有运行中的最小时间(即,来自后台进程的入侵最少),因此这两个程序都是如预期的那样高效。您还应该检查生成的asm代码,以查看它们是否等效。具体取决于。如果按值传递复杂对象,则会增加开销。如果代码是内联的,则未使用的参数可能会被优化掉。
#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;
}