【问题标题】:What determines debugger run-time performance决定调试器运行时性能的因素
【发布时间】:2012-02-19 04:14:52
【问题描述】:

我尝试使用 Wing IDE (v.4.1.3) 和 Komodo IDE (v.7.0.0) 调试 Python 3。正如预期的那样,调试器会增加很多运行时开销。但令我惊讶的是,调试器之间的差异如此之大。

这是同一程序的运行时间。没有断点或其他任何东西,只是没有任何实际调试的常规运行:

  • 由 python 解释器执行:26 秒
  • 由调试器 #1 执行:137 秒
  • 由调试器 #2 执行:1143 秒

我将调试器称为匿名 #1 和 #2,以免这成为其中一个无意(并且可能被误导)的广告。

其中一个调试器真的“快”了 8 倍吗?

或者是否存在某种设计权衡,即更快的调试器放弃某些功能、精度、稳健性或其他任何东西,以换取更高的速度?如果是这样,我很想知道这些细节,无论是专门针对 Wing/Komodo,还是一般的 Python 调试器。

【问题讨论】:

  • 也许它在断点处等待?
  • @yak 没有断点。直接跑到最后。
  • 可能的功能并不多。我猜一个只是慢8倍。但可能你必须分析和分析代码才能知道。

标签: python performance debugging python-3.x


【解决方案1】:

进行优化的 Python 调试器与其他任何软件一样:性能方面可能会有所不同(我是 PyDev 的作者,我已经完成了 PyDev 调试器,所以我可以评论它,但不能评论其他的,所以,我只解释一下优化 Python 调试器——因为我花了很多时间优化 PyDev 调试器——我不会真正谈论其他实现,因为我不知道它们是如何实现的已经完成——除了 pdb,但是 pdb 在它遇到断点并且你正在逐步执行它之后并不是真正的快速调试器实现,尽管它通过运行未跟踪的东西来运行良好,直到你真正执行将开始跟踪的代码你的代码)。

特别是,任何“幼稚”的调试器都可以使你的程序变得更慢,只需在每一帧中启用 Python 跟踪并检查每行执行的断点是否匹配(这大致是 pdb 的工作原理:每当你输入上下文时,它' 将跟踪它,并为调用的每一行检查断点是否匹配它,因此,我相信任何期望快速的实现都不能真正依赖它)。

我知道 PyDev 调试器有几个优化...主要的一个如下:当调试器进入一个新框架(即:函数)时,它会检查是否有任何“潜在”断点可能被击中,如果没有,它甚至不会跟踪该函数(另一方面,当程序执行后稍后添加断点时,它必须重新评估所有先前的假设,因为任何当前帧最终都可能跳过断点)。如果它确定应该跟踪某个帧,它将为该帧创建一个新实例,该实例将负责缓存与该帧相关的所有内容(这只有在 Python 2.5 中才有可能,因此,在使用 Python 2.4 时和更早的版本,除非安装了 threadframe 扩展,否则调试器将尝试模拟它,这将使其在 Python 2.4 上变得相当慢)。

此外,PyDev 调试器目前利用 Cython,尽管这仅限于 CPython...Jython、IronPython 和 PyPy 没有利用它),因此,考虑到纯 Python 模式,仍然进行了许多优化(谢天谢地,Cython 与 Python 足够接近,因此只需进行少量更改即可使其在带有 Cython 的 CPython 上运行得更快)。

关于 PyDev 调试器优化演进的一些相关帖子:

http://pydev.blogspot.co.id/2017/03/pydev-560-released-faster-debugger.html

http://pydev.blogspot.co.id/2016/01/pydev-451-debug-faster.html

http://pydev.blogspot.com/2008/02/pydev-debugger-and-psyco-speedups.html

http://pydev.blogspot.com/2005/10/high-speed-debugger.html

无论如何,在适当的位置运行调试器总是会增加一些开销(即使在经过高度优化的情况下,例如 PyDev 调试器),因此,PyDev 还提供了可以在 pdb 中使用的相同方法:在代码中添加断点,然后' 只会在该点开始跟踪(这是 PyDev 的远程调试器功能):http://pydev.org/manual_adv_remote_debugger.html

并且根据您希望调试器支持的功能,它也可能更慢(例如:当您在 PyDev 中为捕获的异常启用中断时,程序将执行得更慢,因为它需要按顺序跟踪更多的东西正确打破)。

【讨论】:

  • 这是一个完美的答案 - 有时我会等待很长时间(甚至 3-4 个数量级)让我的代码到达特定断点并从磁盘加载其数据(我几乎总是在使用时测试较小的集合调试器),但在这里我找到了一个解决方案 - 只需在函数中放置一个断点,而不是在脚本的顶层,瞧!现在它真的很快。非常感谢您的回答。
【解决方案2】:

这就像问为什么程序 A 比程序 B 快。原因可能不是特定于调试领域。

我想大多数图形调试器都构建在 Python 标准库中提供的 pdb 模块的顶部或前端(尽管并非全部如此)。性能差异主要归结为实现细节和 GUI 更新开销。区别可能很简单,就像在代码的某些层中进行不必要的深层复制与直接引用一样。

如果您担心调试器的性能,那么您应该坚持使用更快的图形调试器,除非它不能满足您需要的某些功能。您要求功能差异;既然是这种情况,它们显然都在满足您当前的功能需求。

如果一个调试器正在失去精度或牺牲稳健性,那么我会立即不考虑它。我可能会猜测速度较慢的是:

  • 进行额外的工作或不必要的检查(如果开发人员不信任代码的其他部分,或者它们的层数过多或架构冗余)。

  • 使用了一些对缓存不友好或浪费的表示,或者没有像更快的调试器那样在算法上调整或优化代码。正确的数据结构选择和算法优化可以产生数量级的差异。使用低级优化(如 ctypes、psyco、pyrex)等,您可以产生另一个数量级的差异。请记住,python 为您提供了灵活而强大的默认容器,即使您不需要它们的所有功能,您也总是“付费”。

我发现WinPDB 相当轻量级,是我经常使用的,因为它不会将我绑定到 IDE,并且非常有效地支持远程调试。您可能还想用pydev 尝试eclipse。我刚开始玩的另一个新的是Python Tools for Visual Studio,它看起来很有前途。 python wiki 上还有一个list of python debuggers。其他人可能值得一试。

至于为什么一个比另一个快,有很多可能的因素。如果您有权访问调试器的源代码,则可能能够分析调试器本身以识别性能瓶颈。但是,如果您只是想要一个能够处理所有基本用例的快速调试器,只需多尝试几个,并坚持使用满足您需求的最快的调试器。

【讨论】:

  • 这是很好的解释,谢谢。现在我很好奇 - 是什么导致更快的调试器中的 6 倍运行时开销?毕竟,我没有任何断点。调试器需要做的就是捕获异常(如果有),并能够告诉我调用堆栈每一层的所有变量的值。物理名称和变量名称之间的小映射还不够吗?
  • @max 不,取决于实现,您可能有很多开销,例如保持和检查堆栈状态的模型或表示以及当前堆栈帧,何时中断等。如果调试器代码是在高抽象级别构建的,那么当您“远离金属”时,可能会有很大的开销。当您通过优化“更接近金属”时,例如使用使用低级操作系统和硬件功能的 c 扩展,您将失去可移植性(即它不会是纯 python)
  • 你可能想看看这本书:[Gray Hat Python] (amazon.com/Gray-Hat-Python-Programming-Engineers/dp/1593271921);特别是第 3 章,这是对在 python 中实现调试器的基础知识的精彩介绍
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-06
  • 1970-01-01
相关资源
最近更新 更多