我的c++扩展在faulthand中的行为不同

2024-05-15 08:55:24 发布

您现在位置:Python中文网/ 问答频道 /正文

背景

我有一个C++扩展,它在缓冲区上运行一个3D分水岭。它有一个很好的Cython包装器来初始化一个由signed chars组成的大缓冲区来表示体素。我在Python中初始化一些本地数据结构(在一个编译的Cython文件中),然后调用一个C++函数初始化缓冲区,另一个实际运行算法(我也可以在Cython写这些,但是我希望它也能作为一个C++库,而不用Python .h依赖)。

怪异

我正在调试我的代码,尝试使用不同的图像大小来衡量RAM的使用和速度等等,我注意到了一些非常奇怪的结果——它们的变化取决于我是使用python test.py(特别是MacOSX10.7.5/Lion上的/usr/bin/python,这是Python2.7)还是{},然后运行{},并在上面调用一个函数(事实上,在我的笔记本电脑上(OS X 10.6.latest,macports Python2.7),结果也有着决定性的不同——每个平台/情况都是不同的,但每个平台/情况都是一样的。)。在所有情况下,调用相同的函数,从文件加载一些输入数据,并运行C++模块。在

关于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!
...

但是

pythonimport testtest.run()

^{pr2}$

这在交互式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,我正在努力寻找解决方案。如果我找到一些可以让人们学习的东西,我会把它作为答案贴出来。在


Tags: and文件数据函数run代码pytest
1条回答
网友
1楼 · 发布于 2024-05-15 08:55:24

在对这个应用程序进行了广泛的调试之后(printf()在运行期间的多个点输出所有数据,将输出管道传输到日志文件,diff处理日志文件)我发现了导致这种奇怪行为的原因。在

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

顺便说一句,这也是为什么bug从一台机器上消失了,但在另一台机器上继续表现出来,部分是通过调试(这确实应该给我一个线索!)在

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

所以,和往常一样,答案是错误不在库中,而是在代码中的某个地方—在这种情况下,是我的错误,错误地假设我的代码的其他部分将初始化它(他们只是有时会这样做)

相关问题 更多 >