2012-01-03 10 views
11

मेरे पास एक सी फ़ंक्शन है जो mallocs() और फ्लोट की 2 डी सरणी पॉप्युलेट करता है। यह पता और सरणी के आकार "रिटर्न" देता है। हस्ताक्षरक्या मैं अपनी स्मृति के स्वामित्व को लेने के लिए एक numpy ndarray को मजबूर कर सकता हूं?

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

मैं इसे अजगर से कॉल करना चाहते है, इसलिए मैं ctypes का उपयोग करें।

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

मैं कभी नहीं पता चल ctypes के साथ तर्क प्रकार निर्दिष्ट करने के लिए कैसे। मैं केवल प्रत्येक सी फ़ंक्शन के लिए एक पायथन रैपर लिखता हूं, और सुनिश्चित करता हूं कि मुझे रैपर में सही प्रकार मिलते हैं। फ्लोट्स की सरणी स्तंभ-प्रमुख क्रम में एक मैट्रिक्स है, और मैं इसे numpy.ndarray के रूप में प्राप्त करना चाहता हूं। लेकिन यह बहुत बड़ा है, इसलिए मैं सी फंक्शन द्वारा आवंटित स्मृति का उपयोग करना चाहता हूं, इसकी प्रतिलिपि नहीं बनाऊंगा। (मैं तो बस इस StackOverflow जवाब में इस PyBuffer_FromMemory सामान मिला: 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) 

यह मैं सही मूल्यों के साथ एक सरणी देने के लिए लगता है। लेकिन मुझे यकीन है कि यह एक स्मृति रिसाव है।

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

सरणी में स्मृति का स्वामित्व नहीं है। काफी उचित; डिफ़ॉल्ट रूप से, जब सरणी एक बफर से बनाई जाती है, तो यह नहीं होना चाहिए। लेकिन इस मामले में यह चाहिए। जब numpy सरणी हटा दी जाती है, तो मुझे वास्तव में मेरे लिए बफर मेमोरी मुक्त करने के लिए पायथन पसंद है। ऐसा लगता है जैसे मैं ओवांडाटा को सच में मजबूर कर सकता हूं, इसे करना चाहिए, लेकिन ओवंडाटा सेटटेबल नहीं है।

असंतोषजनक समाधान:

  1. get_array_py के फोन करने वाले() स्मृति को मुक्त कराने के लिए जिम्मेदार बनाओ। यह बहुत परेशान है; कॉलर किसी भी अन्य numpy सरणी की तरह इस numpy सरणी का इलाज करने में सक्षम होना चाहिए।

  2. मूल सरणी को get_array_py में एक नई numpy सरणी (अपनी खुद की, अलग स्मृति के साथ) में कॉपी करें, पहले सरणी को हटाएं, और get_array_py() के अंदर मेमोरी को मुक्त करें। मूल सरणी के बजाय प्रतिलिपि वापस करें। यह कष्टप्रद है क्योंकि यह अनावश्यक स्मृति प्रतिलिपि होना चाहिए।

क्या मैं चाहता हूं कि ऐसा करने का कोई तरीका है? मैं सी फ़ंक्शन को स्वयं संशोधित नहीं कर सकता, हालांकि यदि यह सहायक है तो मैं लाइब्रेरी में एक और सी फ़ंक्शन जोड़ सकता हूं।

+0

यह दर्द की दुनिया की तरह लगता है .. मुझे लगता है कि आप [segfault hell] (http://xkcd.com/371/) – wim

+0

के लिए पूछ रहे हैं मैंने सीटीपीएस का उपयोग करके सफलता के बिना भी कोशिश की है। एक पूर्ण अप एक्सटेंशन मॉड्यूल यह संभव बनाता है लेकिन वे लिखने के लिए और अधिक काम करते हैं। –

उत्तर

1

मैं दो कार्यों मेरी सी पुस्तकालय से निर्यात हो जाते हैं जाएगा:

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 */ 

मैं तो [1] get_array_c की सरणी आवंटित करने के लिए, तो get_array_c_nomalloc फोन मेरी अजगर आवरण लिखेंगे। फिर पायथन मेमोरी है। आप इस रैपर को अपनी लाइब्रेरी में एकीकृत कर सकते हैं ताकि आपके उपयोगकर्ता को get_array_c_nomalloc के अस्तित्व के बारे में कभी भी अवगत न हो।

[1] यह वास्तव में अब एक रैपर नहीं है, बल्कि इसके बजाय एक एडाप्टर है।

+0

क्षमा करें, मुझे get_array_c() गलत करने के लिए हस्ताक्षर था! यह nroows और ncols के लिए int _pointers_ में लेता है - मुझे नहीं पता कि सरणी कितनी बड़ी होगी, इसलिए मैं अजगर में सरणी को पूर्ववत नहीं कर सकता। –

+0

ठीक है, आप वैकल्पिक रूप से अपने पाइथन रैपर को किसी ऑब्जेक्ट का उपयोग संदर्भ/स्मृति को एक्सेस करने के लिए कर सकते हैं, और सरणी को मुक्त करने के लिए फाइनलज़र का उपयोग कर सकते हैं ... पता नहीं है कि यह आपके सौंदर्य का उल्लंघन करता है या नहीं, लेकिन उपयोगकर्ता ने ' टी को स्पष्ट रूप से स्मृति मुक्त करना है। – Matthew

6

मैं सिर्फ इस सवाल है, जो अभी भी अगस्त 2013 में एक मुद्दा Numpy OWNDATA ध्वज के बारे में सच में picky है पर ठोकर खाई: वहाँ किसी भी तरह से यह अजगर स्तर पर संशोधित किया जा सकता है, तो ctypes सबसे अधिक संभावना नहीं कर सकेंगे यह करने के लिए।numpy सी एपीआई स्तर पर - - और अब हम अजगर विस्तार मॉड्यूल बनाने की एक पूरी तरह से अलग तरीके के बारे में बात कर रहे हैं एक स्पष्ट रूप से ध्वज सेट करने के लिए है:

PyArray_ENABLEFLAGS(arr, NPY_ARRAY_OWNDATA); 

numpy < 1.7 पर, एक था भी होने के लिए अधिक स्पष्ट:

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

एक अंतर्निहित सी समारोह/पुस्तकालय पर कोई नियंत्रण नहीं है, तो सबसे अच्छा समाधान यह अजगर से उपयुक्त आकार के एक खाली numpy सरणी पारित करने के लिए परिणाम स्टोर करने के लिए है बुनियादी सिद्धांत है। पाइथन दुभाषिया के स्तर पर इस मामले में स्मृति आवंटन हमेशा उच्चतम स्तर पर किया जाना चाहिए।


kynan जैसा कि नीचे टिप्पणी की, अगर आप Cython उपयोग करते हैं, तो आप मैन्युअल समारोह PyArray_ENABLEFLAGS बेनकाब करने के लिए, इस पोस्ट Force NumPy ndarray to take ownership of its memory in Cython देखने की है।

प्रासंगिक दस्तावेज here और here है।

+0

मैं साइथन में इसे कैसे प्राप्त करूं? दुर्भाग्यवश 'PyArray_ENABLEFLAGS' को' numpy.pxd' में प्रकट नहीं किया जा रहा है। – kynan

+1

यदि आवश्यक कार्यक्षमता साइथन के संपर्क में नहीं है, तो आप या तो साइथन को पैच कर सकते हैं या सी फ़ाइल को मैन्युअल रूप से उत्पन्न कर सकते हैं। – Stefan

+0

उनमें से कोई भी मेरे लिए बहुत ही टिकाऊ विकल्प प्रतीत नहीं होता है। मैंने अपनी pyx फ़ाइल में 'numpy.pxd' द्वारा खुलासा किया गया विस्तार करने की कोशिश की [लेकिन इसके साथ कोई भाग्य नहीं था] (https://gist.github.com/kynan/ade36155b497c87e0bc5)। – kynan

संबंधित मुद्दे