未使用的默认参数会降低c++的性能吗?
Will unused default arguments decrease performance c++
假设我声明一个函数foo(int arg1, int arg2 = 0, int arg3 = 0, int arg4 = 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: %fn", begin);
printf("nTime: %fn", (float)clock());
return 0;
}
基本上执行函数returnMe
1亿次,然后告诉我花了多长时间。取值范围从280毫秒到318ms。然后我运行这个程序:
#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: %fn", begin);
printf("nTime: %fn", (float)clock());
return 0;
}
大约10次,现在的值在584 ms到624 ms之间。
结论:是的,它会使函数调用变慢,但幅度很小。创建一个单独的函数来传递其他参数给对象,或者有一个不同的构造函数,将会提高性能,但它值得额外的代码吗?
还有另一种解决方法,由Box2D使用,它基本上是为默认参数创建一个单独的结构体,并传递一个指针到它的实例。这样,当不需要设置额外参数时,传递的唯一降低性能的"垃圾参数"是一个空指针,这并不是那么糟糕。当您想要指定一些默认值时,您可以在堆栈中创建所述结构体的实例,填充所需的值,然后将其地址传递给函数。简单、优雅、高效。
然而:两种节省性能的解决方案(一个额外的函数和传递一个结构指针)都需要额外的代码。如果您的函数很少被调用,并且额外的参数没有那么多,那么节省的性能可能根本不会产生任何影响,如果是这种情况,那么不值得您花费时间。只在必要时进行优化。记住,我添加了12个默认参数,甚至没有使函数调用时间翻倍。
= = = = = = = =编辑:对认真测试的奖励。
所以前两个测试是用简单的编译命令g++ test.cpp -o test.exe
完成的。正如在许多评论中指出的那样,这意味着优化级别为- 0。在-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: %fn", begin);
printf("nTime: %fn", (float)clock());
printf("Bar: %in", 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: %fn", begin);
printf("nTime: %fn", (float)clock());
printf("Bar: %in", bar);
return 0;
}
结果:
第一个程序:从653到686毫秒
第二个程序:从652毫秒到735毫秒
正如我所料,第二个程序仍然比第一个程序慢,但是差别现在不那么明显了。
这取决于你的编译器,是否启用了优化,以及函数是否内联。
如果函数/构造函数是内联的,编译器可能会将其优化掉。如果函数不是内联的,则每次调用都会将值压入堆栈,因此它会对性能产生影响(无论是否重要)。
但请记住,过早优化是万恶之源。在运行概要文件之前,不要只是假设这是一个大问题,并编写一个不易维护的代码来绕过它,并确保它需要优化。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- OpenMP阵列性能较差
- 递归列出所有目录中的C++与Python与Ruby的性能
- 大小相等但成员数量不同的结构之间的性能差异
- 为什么constexpr的性能比正常表达式差
- 在类中使用随机生成器时出现性能问题
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 海湾合作委员会 ARM 性能下降
- GCC 和 Clang 代码性能的巨大差异
- 在容量内调整矢量大小时的性能影响
- 了解算法的性能差异(如果以不同的编程语言实现)
- 未达到的情况会影响开关外壳性能
- QStringList vs list<shared_ptr<QString>> 性能比较C++
- 是否总是可以将使用递归编写的程序重写为不使用递归的程序C++,性能观点是什么?
- 哪种方法更好,性能明智
- C++ 特征库:引用的性能开销<>
- 与多个 for 循环与单个 for 循环 wrt 相关的性能从多映射获取数据
- 基于范围的 for 循环range_declaration中各种说明符之间的性能差异
- std::p mr::memory_resource 如何与 std::container 产生性能差异?
- 不同的类或结构初始化方法之间的性能差异是什么?