2012-01-03 25 views
11

Tôi có một hàm C là mallocs() và điền vào một mảng phao nổi 2D. Nó "trả về" địa chỉ đó và kích thước của mảng. Chữ ký làTôi có thể bắt buộc một chiếc máy tính cáu kỉnh để sở hữu trí nhớ của nó không?

int get_array_c(float** addr, int* nrows, int* ncols); 

Tôi muốn gọi nó từ Python, vì vậy tôi sử dụng ctypes.

import ctypes 
mylib = ctypes.cdll.LoadLibrary('mylib.so') 
get_array_c = mylib.get_array_c 

Tôi chưa bao giờ tìm ra cách chỉ định loại đối số với ctypes. Tôi có xu hướng chỉ viết một wrapper python cho mỗi chức năng C tôi đang sử dụng, và chắc chắn rằng tôi nhận được các loại ngay trong wrapper. Mảng float là một ma trận theo thứ tự cột, và tôi muốn lấy nó như là một numpy.ndarray. Nhưng nó khá lớn, vì vậy tôi muốn sử dụng bộ nhớ được cấp phát bởi hàm C, không sao chép nó. (Tôi chỉ tìm thấy những thứ PyBuffer_FromMemory này trong câu trả lời StackOverflow này: https://stackoverflow.com/a/4355701/3691)

buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory 
buffer_from_memory.restype = ctypes.py_object 

import numpy 
def get_array_py(): 
    nrows = ctypes.c_int() 
    ncols = ctypes.c_int() 
    addr_ptr = ctypes.POINTER(ctypes.c_float)() 
    get_array_c(ctypes.byref(addr_ptr), ctypes.byref(nrows), ctypes.byref(ncols)) 
    buf = buffer_from_memory(addr_ptr, 4 * nrows * ncols) 
    return numpy.ndarray((nrows, ncols), dtype=numpy.float32, order='F', 
         buffer=buf) 

này dường như để cho tôi một mảng với các giá trị đúng. Nhưng tôi khá chắc chắn đó là một rò rỉ bộ nhớ.

>>> a = get_array_py() 
>>> a.flags.owndata 
False 

Mảng không sở hữu bộ nhớ. Đủ công bằng; theo mặc định, khi mảng được tạo từ bộ đệm, nó không nên. Nhưng trong trường hợp này nó nên. Khi mảng numpy bị xóa, tôi thực sự thích python để giải phóng bộ nhớ đệm cho tôi. Có vẻ như tôi có thể ép buộc nợ đúng là True, điều đó nên làm, nhưng owndata không thể giải quyết được.

giải pháp đạt yêu cầu:

  1. Tận dụng tối gọi của get_array_py() chịu trách nhiệm giải phóng bộ nhớ. Đó là siêu gây phiền nhiễu; người gọi sẽ có thể xử lý mảng numpy này giống như bất kỳ mảng numpy khác.

  2. Sao chép mảng ban đầu vào mảng mới (với bộ nhớ riêng, riêng) trong get_array_py, xóa mảng đầu tiên và giải phóng bộ nhớ bên trong get_array_py(). Trả lại bản sao thay vì mảng ban đầu. Điều này gây phiền nhiễu vì đó là bản sao bộ nhớ không cần thiết.

Có cách nào để làm những gì tôi muốn không? Tôi không thể sửa đổi các chức năng C chính nó, mặc dù tôi có thể thêm một chức năng C vào thư viện nếu đó là hữu ích.

+0

Điều này nghe có vẻ như một thế giới của đau .. Tôi nghĩ rằng bạn đang yêu cầu [segfault địa ngục] (http://xkcd.com/371/) – wim

+0

Tôi đã thử điều này là tốt mà không thành công bằng cách sử dụng ctypes. Một mô-đun mở rộng đầy đủ làm cho điều này có thể nhưng họ có nhiều việc phải viết hơn. –

Trả lời

1

tôi sẽ có xu hướng có hai chức năng xuất khẩu từ thư viện C của tôi:

int get_array_c_nomalloc(float* addr, int nrows, int ncols); /* Pass addr as argument */ 
int get_array_c(float **addr, int nrows, int ncols); /* Calls function above */ 

Sau đó tôi sẽ viết wrapper Python của tôi [1] của get_array_c phân bổ mảng, sau đó gọi get_array_c_nomalloc. Sau đó, Python không sở hữu bộ nhớ. Bạn có thể tích hợp trình bao bọc này vào thư viện của bạn để người dùng của bạn không bao giờ phải nhận thức được sự tồn tại của get_array_c_nomalloc.

[1] Đây không thực sự là trình bao bọc nữa mà thay vào đó là bộ điều hợp.

+0

Xin lỗi, tôi đã ký tên vào get_array_c() sai! Nó mất trong int _pointers_ cho nrows và ncols - Tôi không biết làm thế nào lớn mảng sẽ được, vì vậy tôi không thể preallocate mảng trong python. –

+0

Vâng, bạn có thể làm cho trình bao python của bạn sử dụng một đối tượng để giữ tham chiếu/truy cập bộ nhớ và sử dụng trình hoàn thiện để giải phóng mảng ... Không biết điều đó vi phạm thẩm mỹ hay không, nhưng người dùng đã thắng ' t phải giải phóng bộ nhớ một cách rõ ràng. – Matthew

6

Tôi chỉ tình cờ gặp câu hỏi này, vẫn còn là vấn đề trong tháng 8 năm 2013. Numpy thực sự cầu kỳ về cờ OWNDATA: Không có cách nào nó có thể được sửa đổi ở cấp độ Python, vì vậy ctypes rất có thể sẽ không thể để làm điều này.Trên NumPy mức C-API - và bây giờ chúng ta đang nói về một cách hoàn toàn khác nhau làm cho các module mở rộng Python - người ta phải thiết lập một cách rõ ràng cờ với:

PyArray_ENABLEFLAGS(arr, NPY_ARRAY_OWNDATA); 

trên NumPy < 1,7, người ta phải được thậm chí rõ ràng hơn:

((PyArrayObject*)arr)->flags |= NPY_OWNDATA; 

Nếu ai có quyền kiểm soát cơ bản chức năng C/thư viện, giải pháp tốt nhất là để vượt qua nó một mảng numPy trống có kích thước thích hợp từ Python để lưu trữ kết quả trong nguyên tắc cơ bản là. việc phân bổ bộ nhớ phải luôn được thực hiện ở mức cao nhất có thể, trong trường hợp này là mức độ của trình thông dịch Python.


Như kynan nhận xét dưới đây, nếu bạn sử dụng Cython, bạn phải tiếp xúc với các chức năng PyArray_ENABLEFLAGS bằng tay, xem bài đăng này Force NumPy ndarray to take ownership of its memory in Cython.

Tài liệu liên quan là herehere.

+0

Làm thế nào tôi có thể đạt được điều tương tự trong Cython? Thật không may 'PyArray_ENABLEFLAGS' dường như không được hiển thị trong' numpy.pxd'. – kynan

+1

Nếu chức năng yêu cầu không được tiếp xúc với Cython, bạn có thể vá Cython hoặc chỉnh sửa tệp C mà nó tạo theo cách thủ công. – Stefan

+0

Không ai trong số đó có vẻ là lựa chọn rất bền vững đối với tôi. Tôi đã thử mở rộng những gì được tiếp xúc bởi 'numpy.pxd' trong tệp pyx của tôi [nhưng không có may mắn với điều đó] (https://gist.github.com/kynan/ade36155b497c87e0bc5). – kynan

Các vấn đề liên quan