簡體   English   中英

Cython 與 C++ 接口:大數組的分段錯誤

[英]Cython interfaced with C++: segmentation fault for large arrays

我正在將我的代碼從使用 ctypes 連接的 Python/C 轉移到使用 Cython 連接的 Python/C++。 新界面將使我更易於維護代碼,因為我可以利用所有 C++ 功能並且只需要相對較少的界面代碼行。

接口代碼與小數組完美配合。 但是,在使用大型數組時會遇到分段錯誤 我一直在思考這個問題,但還沒有接近解決方案。 我已經包含了一個發生分段錯誤的最小示例。 請注意,它始終發生在 Linux 和 Mac 上,並且 valgrind 也沒有給出見解。 另請注意,純 C++ 中完全相同的示例確實可以正常工作。

該示例包含(部分)C++ 中的稀疏矩陣類。 在 Cython 中創建了一個接口。 因此,該類可以從 Python 中使用。

C++端

sparse.h

#ifndef SPARSE_H
#define SPARSE_H

#include <iostream>
#include <cstdio>

using namespace std;

class Sparse {

  public:
    int* data;
    int  nnz;

    Sparse();
    ~Sparse();
    Sparse(int* data, int nnz);
    void view(void);

};

#endif

sparse.cpp

#include "sparse.h"

Sparse::Sparse()
{
  data = NULL;
  nnz  = 0   ;
}

Sparse::~Sparse() {}

Sparse::Sparse(int* Data, int NNZ)
{
  nnz  = NNZ ;
  data = Data;
}

void Sparse::view(void)
{

  int i;

  for ( i=0 ; i<nnz ; i++ )
    printf("(%3d) %d\n",i,data[i]);

}

Cython 接口

csparse.pyx

import  numpy as np
cimport numpy as np

# UNCOMMENT TO FIX
#from cpython cimport Py_INCREF

cdef extern from "sparse.h":
  cdef cppclass Sparse:
    Sparse(int*, int) except +
    int* data
    int  nnz
    void view()


cdef class PySparse:

  cdef Sparse *ptr

  def __cinit__(self,**kwargs):

    cdef np.ndarray[np.int32_t, ndim=1, mode="c"] data

    data = kwargs['data'].astype(np.int32)

    # UNCOMMENT TO FIX
    #Py_INCREF(data)

    self.ptr = new Sparse(
      <int*> data.data if data is not None else NULL,
      data.shape[0],
    )

  def __dealloc__(self):
    del self.ptr

  def view(self):
    self.ptr.view()

setup.py

from distutils.core import setup, Extension
from Cython.Build   import cythonize

setup(ext_modules = cythonize(Extension(
  "csparse",
  sources=["csparse.pyx", "sparse.cpp"],
  language="c++",
)))

Python端

import numpy as np
import csparse

data = np.arange(100000,dtype='int32')

matrix = csparse.PySparse(
  data = data
)

matrix.view() # --> segmentation fault

跑步:

$ python setup.py build_ext --inplace
$ python example.py

請注意, data = np.arange(100,dtype='int32')確實有效

內存由您的 numpy 數組管理。 一旦它們超出范圍(很可能在PySparse構造函數的末尾),數組就不再存在,並且您的所有指針都無效。 這適用於大數組和小數組,但大概你只是對小數組很幸運。

您需要持有對您在PySparse對象的生命周期中使用的所有 numpy 數組的引用:

cdef class PySparse:

  # ----------------------------------------------------------------------------

  cdef Sparse *ptr
  cdef object _held_reference # added

  # ----------------------------------------------------------------------------

  def __cinit__(self,**kwargs):
      # ....
      # your constructor code code goes here, unchanged...
      # ....

      self._held_reference = [data] # add any other numpy arrays you use to this list

通常,在處理 C/C++ 指針時,您需要非常認真地考慮誰擁有什么,這與普通 Python 方法相比是一個很大的變化。 從 numpy 數組獲取指針不會復制數據,也不會向 numpy 提供您仍在使用數據的任何指示。


編輯說明:在我的原始版本中,我嘗試使用locals()作為收集我想要保留的所有數組的集合的快速方法。 不幸的是,這似乎沒有包含在cdef ed 數組中,因此它無法保留您實際使用的數組(請注意,這里astype()會制作一個副本,除非您另有說明,因此您需要保留引用到副本,而不是作為參數傳入的原始文件)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM