我的c ++扩展与faulthandler的行为不同

时间:2012-12-04 20:39:11

标签: c++ python cython

背景

我有一个C ++扩展,它在缓冲区上运行3D分水岭传递。它有一个很好的Cython包装器来初始化signed char s的大量缓冲区来表示体素。我在python中初始化了一些原生数据结构(在一个已编译的cython文件中),然后调用一个C ++函数初始化缓冲区,另一个实际运行算法(我也可以在Cython中编写这些,但我希望它在没有python.h依赖的情况下也可以作为C ++库工作。)

怪诞

我正在调试我的代码,尝试不同的图像大小来衡量RAM的使用和速度等等,我注意到结果有些奇怪 - 它们会根据我是否使用{{1}而改变(在Mac OS X 10.7.5 / Lion上具体为python test.py,即python 2.7)或/usr/bin/python并运行python,并在其上调用函数(实际上,在我的笔记本电脑上) (OS X 10.6.latest,使用macports python 2.7)结果也是确定性的不同 - 每个平台/情况都不同,但每个平台/情况总是与自身相同。)。在所有情况下,调用相同的函数,从文件加载一些输入数据,并运行C ++模块。

关于64位python的注释 - 我没有使用distutils来编译这段代码,而是类似于我的回答here(即明确的import test调用)。这不应该是任何意义,我在Activity Monitor中的所有进程都称为-arch x86_64

正如您所知,分水岭的目的是在像素汤中找到物体 - 在2D中它经常用在照片上。在这里,我用它以大致相同的方式在3D中找到肿块 - 我从图像中的一些肿块(“颗粒”)开始,我想在它们之间的空间中找到反向肿块(“细胞”)。

结果改变的方式是我确实找到了不同数量的肿块。对于完全相同的输入数据:

Intel (64-bit)

python test.py

然而,

grain count: 1434 seemed to have 8000000 voxels, with average value 0.8398655 find cells: running watershed algorithm... found 1242 cells from 1434 original grains! ... pythonimport test

test.run()

在交互式python shell和grain count: 1434 seemed to have 8000000 voxels, with average value 0.8398655 find cells: running watershed algorithm... found 927 cells from 1434 original grains! ... 中也是如此,我原本认为应该责怪它。

注意“平均值”数字完全相同 - 这表示相同比例的体素最初被标记为问题空间 - 即我的输入文件初始化(非常非常可能)完全相同的方式两个时间都在体素空间。

另请注意,算法的任何部分都不是非确定性的;没有随机数或近似值;受浮点误差影响(每次应该相同)我们应该在完全相同的数字上执行完全相同的计算。 Watershed使用一个大的整数缓冲区运行(这里是bpython s),结果是计算这些整数的集群,所有这些都在一个大的C ++调用中实现。

我测试了相关模块对象的signed char属性(它们本身就是导入的__file__的属性),并且它们指向我系统中的同一个已安装的test watershed.so

问题

我甚至不知道从哪里开始调试 - 如何使用相同的输入数据调用相同的函数并获得不同的结果? - 交互式python可能导致这种情况(例如通过改变数据的初始化方式)? - (相当大)代码库的哪些部分与这些问题相关?

根据我的经验,在stackoverflow问题中发布所有代码会更有用,而不是假设您知道问题所在。但是,这里有成千上万行代码,我根本不知道从哪里开始!我很乐意根据要求发布小片段。

我也很高兴听到调试策略 - 我可以检查的解释器状态,python可能影响导入的C ++二进制文件的方式等等。

以下是代码的结构:

site-packages

(标记为project/ clibs/ custom_types/ adjacency.cpp (and hpp) << graph adjacency (2nd pass; irrelevant = irr) *array.c (and h) << dynamic array of void*s *bit_vector.c (and h) << int* as bitfield with helper functions polyhedron.cpp (and hpp) << for voxel initialisation; convex hull result smallest_ints.cpp (and hpp) << for voxel entity affiliation tracking (irr) custom_types.cpp (and hpp) << wraps all files in custom_types/ delaunay.cpp (and hpp) << marshals calls to stripack.f90 *stripack.f90 (and h) << for computing the convex hulls of grains tensors/ *D3Vector.cpp (and hpp) << 3D double vector impl with operators watershed.cpp (and hpp) << main algorithm entry points (ini, +two passes) pywat/ __init__.py watershed.pyx << cython class, python entry points. geometric_graph.py << python code for post processing (irr) setup.py << compile and install directives test/ test.py << entry point for testing installed lib 的文件已在其他项目中广泛使用,并且经过了充分测试,后缀*包含的代码仅在引发问题后才运行。)

详情

根据要求,irr中的主要部分:

test/test.py

我的交互式调用如下:

testfile = 'valid_filename'

if __name__ == "__main__":
  # handles segfaults...
  import faulthandler
  faulthandler.enable()
  run(testfile)

线索信息

当我在直接翻译处运行时:

import test
test.run(test.testfile)

我从文件调用(即1242个单元格)中得到结果,但是当我在bpython中运行它时,它只是崩溃了。

这显然是问题的根源 - 对Ignacio Vazquez-Abrams直接提出正确问题的看法。

更新

opened a bug on the faulthandler github我正努力寻求解决方案。如果我找到了人们可以学习的东西,我会将其作为答案发布。

1 个答案:

答案 0 :(得分:1)

广泛调试此应用程序后(printf()运行期间多个点的所有数据,管道输出到日志文件,diff日志文件)我发现似乎导致奇怪的行为。

我在几个地方使用了未初始化的内存,并且(出于某种奇怪的原因)这给了我上面描述的两个案例之间可重复的行为差异 - 一个没有faulthandler和一个人。

顺便说一下,这也是错误从一台机器上消失的原因,但是继续在另一台机器上表现出来,部分通过调试(这真的应该给我一个线索!)

我的错误在于假设基于虚假相关性的问题 - 理论上每次我访问它时垃圾桶应该是不同的随机(啊,理论)。在这种情况下,我会更快找到打印输出主计算函数和rubber duck的问题。

所以,像往常一样,答案是错误不在库中,它在代码中的某个地方 - 在这种情况下,malloc()大块是我的错RAM,错误地认为我的代码的其他部分将初始化它(他们有时只做它。)