匹配开始/结束分析调用

Posted

技术标签:

【中文标题】匹配开始/结束分析调用【英文标题】:Matching Start / End Profiling Calls 【发布时间】:2011-07-06 19:30:47 【问题描述】:

我目前正在将分析系统实施到应用程序中。

我有两个基于编译器标志 (NDEBUG) 定义的宏函数。当未定义 NDEBUG 时,这两个函数(profilingStart / profilingEnd)会生成分析报告,显示调用 profilingStart 的时间与调用 profilingEnd 的时间。

担心可能发生不匹配 - 即,当 profilingStart 已被调用但 profilingEnd 未调用时(反之亦然)。我的代码会在运行时识别出这些情况,但如果在编译时由于这种不匹配而导致错误会更好。

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

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

一如既往地感谢任何 cmets。提前致谢。

编辑:我对范围的评论可能有些混乱。 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

【问题讨论】:

匹配开始和结束调用:这分别是构造函数和析构函数的工作。大概您已经考虑过这一点,并将其视为解决问题的方法。为什么? @Alf:这并不能真正解决 OP 的问题。您仍然必须在正确的位置调用delete,或者人为地引入额外的嵌套范围以在正确的时间自动调用析构函数。 @jmein 那是我的目标。 另外,您的第二种方法仍然存在范围问题,即 start 和 end 函数必须在同一个范围块内发出(否则 end 函数可能看不到 start 变量)。换句话说,您不能在一种方法中调用 start 并在另一种方法中结束。您是否有不想使用 gprof 的原因? @Pace:Gprof (AFAIK) 具有函数级粒度。 【参考方案1】:

在 C++ 中,一种解决方案是使用“RAII”习语。像这样的:

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

然后你这样使用它:

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

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

不过,它确实需要将您想要分析的代码放在一个块中。

[编辑]

我错过了您希望能够将profilingEnd 放在与profilingStart 不同的块中。

有关如何处理此问题的想法,请参阅下面的 @Roddy 评论;例如通过让析构函数检查以确保在对象被析构时已停止探查器。虽然这不会在编译时捕捉到问题,但它会在运行时捕捉到“接近”问题。

【讨论】:

如果“结束”与“开始”在不同的功能/范围内,这将不起作用(OP 提到这是可能的)。 @Oli Charlesworth -- 我的原始帖子可能存在混淆。我已经澄清了。 您可以通过向对象添加 stop() 方法(可能还有手动启动函数以及构造函数的自动启动参数:Profiler(bool autoStart = true) ...)来避免“在不同范围内结束”问题在范围到期之前明确停止它。在析构函数中,只需检查是否已经调用了 stop。然后您将获得灵活性和 RAII 的易用性和异常安全性。【参考方案2】:

为什么不直接在构造函数中创建一个对象,在构造函数中开始profile事件,在析构函数中结束,然后使用类似于scoped_lock的类,可以确保开始总是成对的。并且看到您可以创建任意范围,您可以在任何地方执行此操作

【讨论】:

如果“结束”与“开始”在不同的功能/范围内,这将不起作用(OP 提到这是可能的)。 @oli:他只提到了作用域,而不是跨函数调用,这几乎是不可避免的,除非你准备好以旧 C 风格转发声明所有受影响的变量(这只会是有害的,如果构造函数很昂贵或有阴影)。 @Necrolis 没错,我没有尝试执行跨函数调用。【参考方案3】:

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

我希望您知道测量执行时间有其自身的用途, 但这是找到“瓶颈”的一种非常间接的方式。 (我更喜欢“时间消耗”。程序慢不是因为它们有狭窄的地方,而是因为它们随意做的事情比他们必须做的多得多。)

Here's a little more on the issues.

【讨论】:

以上是关于匹配开始/结束分析调用的主要内容,如果未能解决你的问题,请参考以下文章

如何分析代码段?

Android深入源码分析理解Aidl整体调用流程(雷惊风)

UINavigationController:使用 SWRevealViewController 错误:不平衡调用开始/结束外观转换

CKPresentationControllerRootViewController 开始/结束外观转换的不平衡调用

对 ParentViewController 的开始/结束外观转换的不平衡调用

开始/结束外观转换的不平衡调用...- UIViewController 包含