背景
我有一个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!
...
,python
,import 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我正努力寻求解决方案。如果我找到了人们可以学习的东西,我会将其作为答案发布。
答案 0 :(得分:1)
广泛调试此应用程序后(printf()
运行期间多个点的所有数据,管道输出到日志文件,diff
日志文件)我发现似乎导致奇怪的行为。
我在几个地方使用了未初始化的内存,并且(出于某种奇怪的原因)这给了我上面描述的两个案例之间可重复的行为差异 - 一个没有faulthandler
和一个人。
顺便说一下,这也是错误从一台机器上消失的原因,但是继续在另一台机器上表现出来,部分通过调试(这真的应该给我一个线索!)
我的错误在于假设基于虚假相关性的问题 - 理论上每次我访问它时垃圾桶应该是不同的随机(啊,理论)。在这种情况下,我会更快找到打印输出主计算函数和rubber duck的问题。
所以,像往常一样,答案是错误不在库中,它在代码中的某个地方 - 在这种情况下,malloc()
大块是我的错RAM,错误地认为我的代码的其他部分将初始化它(他们有时只做它。)