c++ - 我的c++扩展程序与Faulthandler的行为不同

标签 c++ python cython

背景

我有一个C++扩展,可以在缓冲区上运行3D分水岭。它有一个不错的Cython包装器,用于初始化大量signed char的缓冲区以表示体素。我在python中初始化了一些本机数据结构(在已编译的cython文件中),然后调用一个C++函数来初始化缓冲区,然后调用另一个C++函数来实际运行算法(我也可以在Cython中编写这些函数,但是我想用无需python.h依赖也可以用作C++库。)

古怪的

我正在调试我的代码,尝试使用不同的图像大小来衡量RAM使用情况和速度等,并且我注意到结果有些奇怪-它们的变化取决于我是否使用python test.py(特别是在Mac OS上为/usr/bin/python) X 10.7.5 / Lion,即python 2.7)或python并运行import test,并在其上调用一个函数(实际上,在我的笔记本电脑上(使用Macports python 2.7的OS X 10.6.latest),结果在确定性上也有所不同-每个平台/位置都不同,但每个平台/位置始终相同。)在所有情况下,都将调用相同的函数,从文件中加载一些输入数据,然后运行C++模块。

关于64位python的注释-我没有使用distutils来编译此代码,而是类似于我的答案here(即使用显式的-arch x86_64调用)。这并不意味着什么,我在 Activity 监视器中的所有进程都称为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!
...

然而,
pythonimport testtest.run():
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!
...

在交互式python shell和bpython中,这是相同的,我原本以为是罪魁祸首。

请注意,“平均值”数字完全相同-这表明最初在问题空间中标记了相同比例的体素-即,我的输入文件在两次输入中(非常可能)完全相同地初始化。体素空间。

还要注意,算法的任何部分都不是确定性的。没有随机数或近似值;受浮点误差(每次都应该相同)的影响,我们应该两次对完全相同的数字执行完全相同的计算。分水岭使用一个很大的整数缓冲区(此处为signed char)运行,结果是计算这些整数的簇,所有这些都在一个大型C++调用中实现。

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

问题

我什至不知道从哪里开始调试它-如何用相同的输入数据调用相同的函数并获得不同的结果? -交互式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

(标记为*的文件已在其他项目中广泛使用,并且经过了很好的测试,这些后缀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中运行它时,它只是崩溃了。

这显然是问题的根源-伊格纳西奥·巴斯克斯(Ignacio Vazquez-Abrams)立即提出正确的问题。

更新:

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

最佳答案

广泛调试该应用程序之后(在运行过程中printf()在所有点上提取所有数据,将输出管道传输到日志文件,diff在日志文件中),我发现似乎引起异常行为的原因。

我在几个地方使用了未初始化的内存,并且(出于某些奇怪的原因),这使我在上述两种情况之间可重复的行为差异–一种是没有faulthandler的情况,另一种是。

顺便说一句,这也是为什么该错误从一台机器上消失了,但在调试过程中继续在另一台机器上显现出来的原因(这确实应该给我一个提示!)

我在这里的错误是基于虚假相关性来假设有关问题的事情-理论上,每次访问垃圾ram的随机内存都应该是不同的(理论上是啊)。在这种情况下,我会更快地用主要计算功能的打印输出和一个rubber duck

因此,像往常一样,答案是该错误不在库中,而是在您的代码中-在这种情况下,malloc()占用了一块RAM是我的错,错误地认为我的代码的其他部分要初始化它(他们有时只是这样做)。

关于c++ - 我的c++扩展程序与Faulthandler的行为不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13711463/

相关文章:

python - ValueError : setting an array element with a sequence. 决策树

python - 在不产生 python 开销的情况下在 Cython 中定义 NumPy 数组

python - 用 Python 封装一个 C 程序,将自定义文件读取到二维数组中

c++ - 由于 GTest 拆卸,智能指针被过早删除

c++ - 是否有针对 "conditional expression is constant"的 gcc 警告?

c++ - 在 Alpine Linux 上错误地声明了 strerror_r

c++ - 用C++在文件中输入长短语

python - 如何在结构中以并行模式运行一次命令?

python - 如何临时将 pandas df 转换为小写以进行重复检查?然后恢复正常

python - Cython 属性错误 : 'module' object has no attribute 'declare'