【问题标题】:Visual Studio - Runtime impact of conditional and disabled breakpointsVisual Studio - 条件断点和禁用断点的运行时影响
【发布时间】:2009-10-19 15:50:32
【问题描述】:

花了一点时间想知道为什么我的应用程序在附加调试器的情况下运行一个特定的场景非常缓慢,我发现这是由于有一个条件断点(其条件从未得到满足)。这似乎是合理的,因为 CPU 会发出断点信号,而 VS 需要在允许继续执行之前评估条件。这些转变必须付出高昂的代价。

我假设代码路径中未执行的断点对运行时没有影响。

所以我的问题是双重的:

  1. 是否有任何资源可以量化与条件断点相关的成本,如果有,是否可以采取任何措施来降低其运行时评估成本?
  2. 是否存在与“禁用”断点相关的任何成本?禁用是指 VS 在装订线中用空心圆圈显示断点标记。

当然,如果我上面提到的任何事情没有意义,那么请指出正确的方向。

【问题讨论】:

    标签: .net visual-studio debugging breakpoints conditional-breakpoint


    【解决方案1】:

    很难量化条件断点的成本。条件断点中的表达式使用与您在监视或立即窗口中键入完全相同的语义进行评估。这种性质的表达式实际上并没有在客户端程序中执行,而是由特定于语言的表达式求值器处理。以有意义的方式描述这些类型的评估是不可能的。

    但是我可以列出一些在调试窗口评估中速度较慢的东西

    • 函数调用:它们是您能做的最慢的事情,因为函数调用需要重新启动被调试进程,以便 func eval 可以在进程中发生
    • 字符串比较:在后台这些返回到 func evals

    至于禁用断点,不,它们不会影响应用程序的运行。

    【讨论】:

    • 评估条件断点会产生调试器中断以及评估用于条件的任何表达式的成本。降低成本的一种方法(如果它在调试应用程序时造成困难)是修改源代码以使用常规 if() 后跟空语句 (;) 来测试条件,然后在那里放置一个非条件断点.这并不总是可行的(显然需要访问源代码),但这是降低条件评估成本的一种方式。
    • @LBushkin,不幸的是,条件断点的成本比这复杂得多。在调试器中键入的表达式以混合样式进行评估,其中一部分出现在被调试者上,一部分出现在调试器中(有时仅在调试器进程中)。很难总结 SO 帖子中发生的舞蹈。但是,是的,您对使用 if() 块进行优化是正确的。当需要复杂的条件时,我会这样做。
    【解决方案2】:

    要注意的一件事(我学到了很难)是确保在将变量与值进行比较时放置 == 而不是单个 =

    断点编辑器不会警告您,但是在评估断点时,变量会被更改!我花了一些时间来调试我的代码!

    另外,如果我真的需要条件断点来调试代码;我将条件添加到代码中,然后添加类似 string stop = "here"; 之类的内容。并在那里放置一个正常的断点 - 我发现代码运行得更快。

    【讨论】:

    • 关于你的最后一点——只有在你可以编辑代码的情况下才有效,并且在出现难以重现的错误的情况下,如果你愿意重新启动该过程。
    【解决方案3】:

    我在某处读到这些断点有硬件支持,因此使用少于 x 个特定类型的条件断点基本上是免费的,但除此之外,它需要使用更多软件。 (OTOH,那是针对本机应用程序的,不确定这些新奇的 JIT 东西。)

    禁用的断点应该会产生影响,它们只是在 IDE 中占用一些内存和 GUI 资源。

    【讨论】:

      【解决方案4】:

      我还注意到条件断点很昂贵,并且得出与您相同的结论。我无法想象禁用断点会导致任何减速的任何原因,因为我希望它只是一个编辑器,是在需要时打开断点的快捷方式。

      当我遇到像你这样的情况时,我会做一个断言宏。 (你可以使用visual studio提供的assert宏,但我不喜欢)。让您的宏检查您想要的条件,然后在失败时调用DebugBreak。在您的应用程序的发布或未经检查的构建中,将断言评估为空,因此您的代码不会受到影响。

      一个简单的断言宏可以如下所示:

      #ifdef _DEBUG
      #define assert(x)\
      do{\
        if(!(x)){DebugBreak();}\
      }while(0)
      #else
      #define assert(x)
      #endif
      

      然后这样称呼它:

      assert(pValue != NULL && "A bad parameter was passed to the function");
      

      您可以在 DebugBreak 之前在失败中添加更多代码(例如使用 #x 打印失败的条件,和/或使用 ____FILE____ 和 ____LINE____ 打印行/文件号,以便您可以双击消息)。您可以使用OutputDebugString 将消息写入调试日志,甚至可以检查是否使用IsDebuggerPresent 附加了调试器以进一步定制您的断言。我还喜欢使用 && 字符串格式来提供有关特定断言的更多信息。

      使用断言时需要注意一些事项。首先,不要在宏中放置任何必须运行的代码,因为它会在非调试版本中被剥离。出于同样的原因,不要放入有副作用的代码。此外,您不希望在未附加调试器时调用 DebugBreak(),因为它本质上会引发异常,如果未捕获该异常,将终止应用程序。

      【讨论】:

        【解决方案5】:
        1. 试着把断点放在你的 测试性能的代码。例如

          Stopwatch st = new Stopwatch();
          st.Start();
          if(my condition)
          {
            st.Stop();
            Debugger.Break();
          }
          

          不,不完全相同,但足够接近。

        2. 否 - 禁用断点不是 存在于执行程序中。 它只是存储在 VS 元数据中 为了您的方便。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-29
          • 2011-05-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-28
          相关资源
          最近更新 更多