要使代码运行得更快,首先要了解运行时实际花费在代码中的哪些位置。但假设,无论出于何种原因,代码分析工具都无法工作?
我最近在遗留的 C++ 代码上使用了 MS Visual Studio。尝试分析时,代码在启动后不久就崩溃了,但在其他方面,代码对于发布和调试构建目标都运行良好。问题的原因尚未立即显现。
如果其他方法都失败了,使用手动计时器会有所帮助。这个想法是找到一个高精度的系统挂钟计时器函数,并使用它来读取您想要计时的代码的某些部分之前和之后的时间。本质上可以对代码库应用“二分搜索”来寻找代码热点。请参阅下面的示例。
这在各种情况下都很有用。复杂语言(甚至代码库中的混合语言)中的代码可能具有破坏调试器或分析器的异常结构。此外,嵌入式系统、GPU 或 FPGA 等特殊硬件可能缺乏完整的分析器支持。此外,全新的硬件版本通常缺乏成熟的工具支持,至少在最初是这样。
此外,分析工具本身虽然有助于快速了解代码中每个函数的性能细分,但也有其自身的局限性。探查器通过检测可执行代码或采样来工作。检测可能会增加在进入和退出时调用系统计时器到每个调用的函数的开销,从而导致计时不准确。它还会破坏函数内联,通常会降低性能。
另一方面,如果采样率太低,采样可能不准确,或者当采样频率太高时,采样可能会扭曲运行时间。相反,手动计时器可以通过对代码的特定部分进行非常外科手术的应用来规避这些问题(尽管某些分析器允许您在代码的不同部分打开和关闭分析器)。
依靠手动计时代码段是一件混乱的事情。但有时这是唯一有效的方法。
Visual Studio C++ 代码示例
// mycode.h #include“cstdio” #include“cstdarg” // 获取一天中的时间 - 经过的秒数 静态双gtod() { LARGE_INTEGER ctr,频率; 查询性能频率(&freq); 查询性能计数器(&ctr); 返回 static_cast(ctr.QuadPart) / static_cast(freq.QuadPart); } // 打印到文件的便捷函数 静态无效 FilePrintf(const char* 格式, ...) { 字符缓冲区[1024]; va_list 参数; va_start(参数,格式); vsnprintf(缓冲区,sizeof(缓冲区),格式,args); va_end(参数); FILE* myoutfile = fopen("mytimingsfile.txt", "a"); fprintf(myoutfile, "%s", 缓冲区); fclose(myoutfile); } // 定时器的存储 外部双 g_timer;
// mycode.cpp #include“mycode.h” // 定时器初始化 双 g_timer = 0; int main() { // ... g_timer = 0; for (int i=0; i<n; ++i) { // ... const double t1 = GTOD(); 我的昂贵函数(); g_timer += gtod() - t1; // ... } FilePrintf("my_expense_function 运行时间:%.6f 秒。\n", g_timer); g_timer = 0; // ...
没有分析器的代码分析一文首先出现在John D. Cook上。
原文: https://www.johndcook.com/blog/2025/02/27/code-profiling-without-a-profiler/