背景
我有一个C++扩展,它在缓冲区上运行一个3D分水岭。它有一个很好的Cython包装器来初始化一个由signed char
s组成的大缓冲区来表示体素。我在Python中初始化一些本地数据结构(在一个编译的Cython文件中),然后调用一个C++函数初始化缓冲区,另一个实际运行算法(我也可以在Cython写这些,但是我希望它也能作为一个C++库,而不用Python .h依赖)。
怪异
我正在调试我的代码,尝试使用不同的图像大小来衡量RAM的使用和速度等等,我注意到了一些非常奇怪的结果——它们的变化取决于我是使用python test.py
(特别是MacOSX10.7.5/Lion上的/usr/bin/python
,这是Python2.7)还是{
关于64位python的一个注释-我不是使用distutils来编译这段代码,而是类似于我的答案here(即使用显式的-arch x86_64
调用)。这不意味着什么,我在活动监视器中的所有进程都被称为Intel (64-bit)
。
如你所知,分水岭的重点是在像素汤里找到物体——在2D中,它经常被用在照片上。在这里,我用它来寻找3D中的块状物,用的方法大致相同——我从图像中的一些块状物(“颗粒物”)开始,我想在它们之间的空间中找到相反的块状物(“细胞”)。在
结果变化的方式是我发现了不同数量的肿块。对于完全相同的输入数据:
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()
:
这在交互式pythonshell和bpython
中是一样的,我最初认为这是罪魁祸首。在
注意“平均值”数字是完全相同的-这表明相同部分的体素最初被标记为问题空间-即,我的输入文件初始化的方式(非常可能)与体素空间中的两次完全相同。在
还要注意的是,算法的任何部分都是不确定的;没有随机数或近似值;由于浮点错误(每次都应该相同),我们两次应该在完全相同的数字上执行完全相同的计算。分水岭使用整数的大缓冲(这里^ {< CD1> }s),结果是对这些整数的簇进行计数,所有这些都是在一个大C++调用中实现的。在
我已经测试了相关模块对象的__file__
属性(它们本身就是导入的test
的属性),它们指向的是我系统的site-packages
中安装的相同的watershed.so
。在
问题
我甚至不知道从哪里开始调试这个-怎么可能用相同的输入数据调用同一个函数并得到不同的结果?-交互式python会导致这种情况吗(例如,通过改变数据初始化的方式)?-代码库的哪些部分(相当大)与这些问题相关?在
根据我的经验,在stackoverflow问题中发布所有代码,而不是假设您知道问题在哪里,这会更有用。但是,这里有数千行代码,我根本不知道从哪里开始!我很乐意按要求发布小片段。在
<>我也很高兴听到调试策略——我可以检查的解释状态,Python可能影响导入的C++二进制文件的详细信息,等等。在代码结构如下:
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
(标记为*
的文件已被e从更广泛的角度来看,在其他项目中,那些后缀为irr
的代码只在问题产生后才运行
详细信息
根据要求,test/test.py
中的主要段落:
testfile = 'valid_filename'
if __name__ == "__main__":
# handles segfaults...
import faulthandler
faulthandler.enable()
run(testfile)
我的交互式调用看起来像:
import test
test.run(test.testfile)
线索
当我在直译机上运行这个程序时:
import faulthandler
faulthandler.enable()
import test
test.run(test.testfile)
我从文件调用中得到结果(即1242个单元格),尽管在bpython中运行它时,它只是崩溃。在
这显然是问题的根源——向伊格纳西奥·巴斯克斯·艾布拉姆斯致敬,因为他直接提出了正确的问题。在
更新:
我已经opened a bug on the faulthandler github,我正在努力寻找解决方案。如果我找到一些可以让人们学习的东西,我会把它作为答案贴出来。在
在对这个应用程序进行了广泛的调试之后(
printf()
在运行期间的多个点输出所有数据,将输出管道传输到日志文件,diff
处理日志文件)我发现了导致这种奇怪行为的原因。在我在一些地方使用了未初始化的记忆,并且(出于某种奇怪的原因)这给了我在上面描述的两个案例之间可重复的行为差异-一个没有
faulthandler
和一个有。在顺便说一句,这也是为什么bug从一台机器上消失了,但在另一台机器上继续表现出来,部分是通过调试(这确实应该给我一个线索!)在
我在这里的错误是假设问题是基于一个虚假的相关性-理论上,垃圾ram应该是不同的随机性每次我访问它(啊,理论上)。在这种情况下,我会更快地找到问题与打印输出的主计算函数和rubber duck。在
所以,和往常一样,答案是错误不在库中,而是在代码中的某个地方—在这种情况下,是我的错误,错误地假设我的代码的其他部分将初始化它(他们只是有时会这样做)
相关问题 更多 >
编程相关推荐