如何在一些Python代码中追踪Heisenbug?

快速背景:我们有一个用Python编写的大型源代码库。 它是针对领域特定语言的编译器,内部一切都以有向图表示。 这些有向图由集合构成,因此我们在Python中使用内建集类型。

问题在于我们最初并没有意识到Python在set对象中主动使用缺乏排序保证来使用更快的非确定性实现。 所以当你迭代集合中的对象时(我们经常这样做),它们返回的顺序是弱随机的。 它在每次执行时都不会改变,但它确实经常改变。 根据我所看到的调试我们的代码,似乎源代码的散列作为随机数生成器的种子。 因此,即使在未执行的路径中更改代码,也会导致集迭代器更改生成元素的顺序。

我们的基本调试策略是将打印输出到我们认为错误的地方,并根据输出对输出进行改进,直到找到错误。 不是很优雅,但对于大多数情况下,它在很大程度上起作用 基本问题是添加/更改任何打印语句会触发不同的行为和非常不同的执行路径。 我们不能打印/记录所有内容,因为它会将编译器的执行时间从大约20秒(可管理)降低到大约一个小时(不太管理)。

你如何诊断一个不经常发生的问题,当你开始寻找它时会消失?

编辑澄清 :几个答案建议如何解决集合的排序。 正如torkildr在下面所说:“这里的问题不是集合的行为奇怪,问题是你的程序表现得好像它不行”。 这正是问题所在,但解决方案不是使用确定性集合。 这只会掩盖行为。 问题是要找出为什么我们的程序以这种方式行为并修复这种行为。 我们使用的算法应该适用于以无序集合表示的图。 他们不。 我需要找出这些错误以及它们为何发生的原因。

问题解决了 :事实证明,如果在集合中存储的对象的__eq____hash__方法之间的关系不是一个有效的排序,那么在我使用的Python的实现(OS-X上为2.6)时,系统表现出所描述的弱随机行为。 在set.add()的C实现中必须有一些代码使用随机模块中的某些代码来构建表示。 这会导致依赖于改变磁盘写入顺序的系统熵池。

没有直接的答案,但阅读kriss的后续问题导致洞察力解决这个问题,所以他得到了投票。


为什么不改变影响设置输出顺序的代码,并使用pdb而不是添加打印? 设置断点是否也改变设置顺序? 如果不是,pdb将允许您检查内部变量。

你对你的问题的描述也会导致一些谜团。 你如何检测到有一个错误? 如果这种检测可以在运行时完成,则可能的策略是一旦看到它(使用运行时ifs),就会从代码中运行pdb(与import pdb; pdb.set_trace()一样简单),而不是在随后的更改代码后执行。 这样你就不必更改代码来进行调试(但也许在代码调试和强壮时稍后再删除这些断言)。

顺便说一句,你也应该在编写代码时单元测试你的所有代码,那么隐藏有害的错误的可能性就会小得多。


当你明确知道问题的根源是什么时,我真的不明白为什么你会想进一步考虑问题。

她的主要问题是:您需要确定性的,有序的图表; Python集不会给你这个。 为什么不将实现更改为某个列表/集合的实现?

当你改变代码的时候说它改变了,当你认为set算法可能被优化以使用它认为合适的最快存储模式时,这听起来并不令人意外。 换句话说,你做了一些其他的操作,存储一个变量,打印一行,然后内存改变。

编辑:总结一下; 这里的问题不是集合表现奇怪,问题是你的程序表现得好像没有。


Raymond Hettinger撰写了有序集的配方。 其所有方法的Big-O运行时间与组相同。 您可能想尝试将所有套件更改为订单。

链接地址: http://www.djcxy.com/p/48087.html

上一篇: How do I track down a Heisenbug in some Python code?

下一篇: how to use memcache to speed