在软件开发过程中,性能测试是一个不可或缺的环节。它可以帮助识别代码中的性能瓶颈,从而进行优化。然而,使用复杂的性能分析工具可能会让这个过程变得繁琐。本文将介绍一种简单易用的性能测试工具,适用于C++应用程序。
有时候,只需要一个简单的性能测试工具来满足基本需求。例如,可能想知道ABC模块中哪部分代码执行时间最长,或者比较Xyz算法和Zyx算法的执行时间。在这种情况下,可以使用自定义的性能测试代码,而不必设置复杂的性能分析工具。
希望实现以下功能:
虽然有一些现成的性能测试库,如google/benchmark、Celero和Nonius,但这些库功能强大,可能不适合小型项目或快速实验。本文介绍的方法更适合这些场景。
性能测试的核心是一个好的计时器。以下是一些可用的计时器选项:
应该使用std::high_resolution_clock,但在VS2013中可能无法正常工作。在VS2015中已经修复。如果使用的是最新的编译器/库,那么std::chrono应该可以正常工作。如果使用的是旧工具,那么最好再次检查。
在简单场景中,可能只需要使用printf/cout输出结果。另一种选择是直接记录到某个日志文件或使用Debug View。
测量某些效果可能会改变结果。性能测试代码对经过时间的影响有多大?如果它相对于测量的代码占用了相当长的时间,可能需要以某种方式延迟这个过程。
一个简单的解决方案是:
void longFunction() {
SIMPLEPERF_FUNCSTART;
SIMPLEPERF_START("loop");
for (int i = 0; i < 10; ++i) {
SIMPLEPERF_SCOPED("inside loop");
//::Sleep(10);
internalCall();
}
SIMPLEPERF_END;
}
程序结束时,将显示:
main : 14837.797000
longFunction : 0.120000
loop : 0.109000
inside loop : 0.018000
internalCall : 0.008000
inside loop : 0.011000
internalCall : 0.009000
...
inside loop : 0.005000
internalCall : 0.002000
shortMethod : 15.226000
loop : 15.222000
可以使用以下三个基本宏:
此外,代码支持两种模式:
在保留模式下,可以调用:
需要将#define SIMPLEPERF_SHOWIMMEDIATE设置为true以使用保留模式。
整个计时器可能在多核、多线程代码中不起作用,因为它不使用任何临界部分来保护共享数据,也不关心代码正在运行的线程。如果需要一个更高级的计时器,那么可能对Preshing on Programming上的文章感兴趣:A C++ Profiling Module for Multithreaded APIs。
计时器的核心思想是使用析构函数来收集数据。这样,当某个计时器对象超出范围时,就会得到数据。这对于整个函数/显式作用域非常方便。
{
// scope start
my_perf_timer t;
}
在基本的即时形式中,计时器只是在构造函数中保存时间(使用QueryPerformanceCounter),然后在析构函数中测量结束时间并将其打印到输出。
在保留模式下,还需要存储数据以备将来使用。简单地创建了一个静态向量,在构造函数中添加一个新条目,然后在析构函数中填充最终时间。还注意缩进,以便输出看起来很漂亮。
在仓库中,还有一个头文件版本(有点简化,只使用即时模式):see SimplePerfTimerHeaderOnly.h。
在打印数据时添加文件/行信息? 使用std::chrono for VS2015/GCC版本
本文介绍了一个方便的性能计时器。如果只需要检查代码/系统执行时间,只需包含一个头文件(+添加相关的.cpp文件),并在分析的地方使用SIMPLEPERF_FUNCSTART或SIMPLEPERF_START(str)/END。最终输出应该可以帮助找到热点...而无需使用高级工具/机械。