2012-12-04 41 views
2

背景我的C++擴展與faulthandler

我有運行在緩衝液中的3D分水嶺通一個C++擴展不同的行爲。它有一個很好的Cython包裝來初始化一個巨大的緩衝區signed char s代表體素。我在python中初始化了一些本地數據結構(在編譯的cython文件中),然後調用一個C++函數來初始化緩衝區,另一個實際運行算法(我也可以用Cython寫這些數據結構,但我希望它工作作爲一個C++庫,以及沒有python.h扶養。)

怪誕

我在調試我的代碼,嘗試不同的圖像大小來衡量內存使用和速度等的過程是,並且我注意到結果有些奇怪 - 它們會根據我是否使用python test.py(特別是在Mac OS X 10.7.5/Lion,它是python 2.7)或python上運行(特別是/usr/bin/python),並調用它的功能(事實上,在我的筆記本電腦上(OS X 10.6.latest,macports python 2.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()

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外殼和bpython相同,我原本認爲這是責任。

請注意,「平均值」數字是完全相同的 - 這表示相同比例的體素最初被標記爲問題空間 - 即我的輸入文件被初始化爲(很可能)完全相同在體素空間中都是這樣。

另請注意,該算法的任何部分都是非確定性的;沒有隨機數字或近似值;由於浮點錯誤(每次應該是相同的),我們應該在兩次完全相同的數字上執行完全相同的計算。分水嶺運行時使用大整數緩衝區(這裏是signed char s),結果是對這些整數的簇進行計數,所有這些都是在一個大的C++調用中實現的。

我已經測試了相關模塊的對象(其本身屬性進口test的)的__file__屬性,他們指向同在我的系統的安裝site-packageswatershed.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 

(文件標記爲*已在其他項目中廣泛使用,是很好的測試,這些後綴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中運行它時,它只是崩潰。

這顯然是問題的根源 - 伊格納西奧巴斯克斯 - 艾布拉姆斯馬上要求正確的問題。

UPDATE:

我已經opened a bug on the faulthandler github,我爭取解決工作。如果我找到一些人們可以學習的東西,我會將其作爲答案發布。

+1

主節嚴格調用'run()'嗎?差不多就是 –

+0

。爲兩種情況添加了確切的代碼。 – tehwalrus

+0

看到我的後續編輯 - 調用'faulthandler.enable()'似乎有所作爲!我會去挖掘它的來源[這裏](https://github.com/haypo/faulthandler)尋找答案。 – tehwalrus

回答

1

在廣泛調試此應用程序(printf()在運行過程中的多個點處輸出所有數據,將管道輸出記錄到日誌文件,diff日誌文件)後,我發現似乎導致了奇怪的行爲。

我用的是未初始化的內存在幾個地方,以及(對於一些奇怪的原因),這給了我兩種情況我上面描述之間重複的行爲差異 - 一個沒有faulthandler和一個帶。

順便說一句,這也是爲什麼錯誤從一臺機器消失但繼續表現出來的另一個部分的方式,通過調試(這確實應該給我一個線索!)

我的錯誤這裏是假設基於虛假關聯的問題 - 理論上每次訪問垃圾RAM時應該有不同的隨機性(ahh,理論)。在這種情況下,我會更快地發現主要計算結果的打印輸出問題功能和一個rubber duck

所以,像往常一樣,答案是的錯誤是不是在圖書館,這是什麼地方在你的代碼 - 在這種情況下,這是我對malloc()荷蘭國際集團的RAM塊故障,錯誤地假定其他部分我的代碼將要初始化它(他們只是有時候會這樣做)。