匹配开始/结束分析调用

Matching Start / End Profiling Calls

本文关键字:调用 结束 开始      更新时间:2023-10-16

我目前正在一个应用程序中实现一个分析系统。

我有两个宏函数,它们是基于编译器标志(NDEBUG)定义的。当没有定义NDEBUG时,这两个函数(profilingStart/profilingEnd)生成分析报告,其中显示调用profilingStart的时间和调用profilingEnd的时间。

关注的是发生不匹配的可能性——即,当profilingStart被调用,但profilingEnd没有被调用(反之亦然)的场景。我的代码已经在运行时识别了这些情况,但如果在编译时由于这种不匹配而导致错误,则更可取。

建议使用do{…}while();构造以确保分析函数正确配对。开始宏函数将包含do{,结束宏将包含}while()。如果缺少一个,我们将在编译时得到一个错误。然而,这有一些问题——你只能在被分析的函数的开始和结束时使用profilingStart()和profilingEnd()调用,因为在函数中使用它们可能会影响局部变量的作用域(因为它们可能由于do{…}while()调用而超出作用域)。

我的另一个想法是在profilingStart函数中声明一个变量,然后尝试在profilingEnd函数中修改该变量的内容。这可以防止作用域问题,如果从未声明过变量,则会生成编译器错误。但是,我永远不会有任何方法来验证变量的内容是否在end函数中被修改了。这只解决了一半的问题,因为它没有验证profilingEnd函数的调用。

一如既往,欢迎任何评论。提前谢谢。

EDIT:关于我关于范围的评论可能会有一些混淆。profilingStart()和profilingEnd()总是在同一个函数中被调用。它们可能不会在函数的最开始/最后被调用。下面是我的意思的一个例子:

int DoSomething(void)
{
   profilingStart();
   int a;
   DoMath(a);
   profilingStop();
   return a; // a is out of scope here, as the do{...}while(0) construct has gone out of scope
}

在c++中,一个解决方案是使用"RAII"习语。像这样:

class Profiler {
  public:
    Profiler() { profilingStart(); }
    ~Profiler() { profilingEnd(); }
}

然后像这样使用:

{ // start of block you want to profile
    Profiler prof;
    ...
}

这将确保即使在存在异常、早期返回、break等情况下也会调用profilingEnd。也就是说,它绝对保证调用是成对的。

它确实需要把你想要分析的代码放在一个块中。

[编辑]

我错过了你想把profilingEndprofilingStart放在不同的块中。

看看下面@Roddy的评论,看看如何处理这个问题;例如,通过析构函数检查来确保分析器在对象被析构时已经停止。虽然这不会在编译时捕获问题,但它会在运行时"接近"问题时捕获它。

为什么不直接创建一个对象,在构造函数中启动概要文件事件,并在析构函数中结束它,然后使用类似于scoped_lock的类,您可以确保启动总是配对的。可以创建任意作用域,可以在任意位置

对于你的问题,我推荐@Nemo的答案。使用c++调用析构函数的能力,并坚持基本的词法作用域。

我希望你意识到测量执行时间有它自己的用途,但这是一种寻找"瓶颈"的非常间接的方法。(我更喜欢"浪费时间"。程序之所以慢,不是因为它们的位置狭窄,而是因为它们随意地做了比必须做的更多的事情。