Освобождение памяти при использовании ctypes - PullRequest
0 голосов
/ 30 января 2019

Я использую ctypes, чтобы попытаться ускорить мой код.

Моя проблема похожа на ту, которая описана в этом уроке: https://cvstuff.wordpress.com/2014/11/27/wraping-c-code-with-python-ctypes-memory-and-pointers/

Как указано в уроке, я должен освободитьпамять после использования функции C.Вот мой код C

//C functions
double* getStuff(double *R_list, int items){

    double results[items];  
    double* results_p;   

    for(int i = 0; i < items; i++){ 
            res = calculation ; \\do some calculation
            results[i] = res;   }

        results_p = results;
        printf("C allocated address %p \n", results_p);

return results_p; }

void free_mem(double *a){
    printf("freeing address: %p\n", a);

    free(a);    }

, который я компилирую с gcc -shared -Wl,-lgsl,-soname, simps -o libsimps.so -fPIC simps.c

И python:

//Python
from ctypes import *
import numpy as np


mydll = CDLL("libsimps.so")
mydll.getStuff.restype = POINTER(c_double)
mydll.getStuff.argtypes = [POINTER(c_double),c_int]
mydll.free_mem.restype = None
mydll.free_mem.argtypes = [POINTER(c_double)]

R = np.logspace(np.log10(0.011),1, 100, dtype = float) #input


tracers = c_int(len(R))

R_c = R.ctypes.data_as(POINTER(c_double))


for_list = mydll.getStuff(R_c,tracers)
print 'Python allocated', hex(for_list)
for_list_py = np.array(np.fromiter(for_list, dtype=np.float64, count=len(R)))
mydll.free_mem(for_list)

До последней строки код делает то, что я хочуэто и for_list_py значения являются правильными.Однако, когда я пытаюсь освободить память, я получаю ошибку сегментации , и при ближайшем рассмотрении адрес, связанный с for_list -> hex(for_list), отличается от адреса, выделенного для results_p в C-частикод.

Как указано в этом вопросе, Python ctypes: как освободить память?Ошибка неверного указателя , for_list вернет тот же адрес, если для mydll.getStuff.restype установлено значение c_void_p.Но затем я изо всех сил пытаюсь поместить фактические значения, которые я хочу, в for_list_py.Вот что я пробовал: cast (for_list, POINTER (c_double)) for_list_py = np.array (np.fromiter (for_list, dtype = np.float64, count = len (R))) mydll.free_mem (for_list) , где, похоже, операция приведения превращает for_list в целое число. Я довольно плохо знаком с C и очень растерялся.Нужно ли освободить этот кусок памяти?Если это так, как мне это сделать, сохраняя при этом вывод в виде массива?Спасибо!

Редактировать: Похоже, что адрес, выделенный в C, и адрес, который я пытаюсь освободить, совпадают, хотя я все еще получаю ошибку сегментации.

C allocated address 0x7ffe559a3960
freeing address: 0x7ffe559a3960
Segmentation fault

Если я получу print for_list Я получу <__main__.LP_c_double object at 0x7fe2fc93ab00>

Заключение

Просто чтобы все знали, я боролся сc_types для немного.

В итоге я выбрал SWIG вместо c_types.Я обнаружил, что код в целом работает быстрее (по сравнению с представленной здесь версией).Я нашел эту документацию по работе с освобождением памяти в SWIG очень полезной https://scipy -cookbook.readthedocs.io / items / SWIG_Memory_Deallocation.html , а также тот факт, что SWIG дает вам очень простой способ справиться сnumpy n-мерных массивов.

1 Ответ

0 голосов
/ 30 января 2019

После выхода из функции getStuff память, выделенная для массива results, больше не доступна, поэтому при попытке ее освободить программа вылетает.

Попробуйте вместо этого:

double* getStuff(double *R_list, int items)
{
  double* results_p = malloc(sizeof((*results_p) * (items + 1));
  if (results_p == NULL)
  {
    // handle error
  }

  for(int i = 0; i < items; i++)
  { 
    res = calculation ; \\do some calculation
    results_p[i] = res;
  }

  printf("C allocated address %p \n", results_p);

  return results_p;
}
...