Эффективное преобразование из массива C структуры в массив NumPy с помощью ctypes - PullRequest
0 голосов
/ 12 июня 2018

После этого вопроса , где я пытался использовать C ++ DLL с Cython, с учебником, подходящим для моего случая, который никогда не работал, я решил использовать ctypes.Теперь я успешно вызываю интересующую меня функцию в моей DLL с использованием ctypes благодаря интенсивному просмотру SO.Теперь я столкнулся с проблемой использования результатов, представляющих собой массив struct в Python.

Эта функция C выглядит следующим образом:

void myfun(
                 double         a,
                 //...more double parameters
                 int            max_iter,
                 int *          nb_iter,
                 myStruct *     res_arr,
                 bool *         ok
                );

с myStruct, определенным следующим образом:

typedef struct  {
             double dat;

             int    k;
             int    m;
            // ... more int

             double b;
             double v;
             //...more double

            } myStruct;

Я вызываю эту функцию через следующий код Python:

import ctypes
lib = ctypes.CDLL('PATH_TO_DLL\\lib.dll')

myFunPy = getattr(lib,"?myFun@@YANNNNN_BUNCH_OF_Ns_NNNHPEAHPEAUmyStruct@@PEA_N@Z") # name found through dumpbin.exe (due to C++)

class myStruct(ctypes.Structure):
    _fields_ = [("k", ctypes.c_int),
                ("m", ctypes.c_int),
                #...more int parameters

                ("b", ctypes.c_double),
                ("v", ctypes.c_double)
                #...more double parameters
               ]

myFunPy.argtypes = [ctypes.c_double,
                   // ... more double parameters

                   ctypes.c_int,
                   ctypes.POINTER(ctypes.c_int),
                   ctypes.POINTER(myStruct),
                   ctypes.POINTER(ctypes.c_bool)]

myFunPy.restype = ctypes.c_void_p

max_iter = 10000
a = ctypes.c_double(0.1)
// ... more double parameters definitions

nb_iter = ctypes.c_int(0) # value doesn't matter, it is initialized in myFun
ok = ctypes.c_bool(True)

res_arr = (myStruct * max_iter)()

myFunPy(a, ..., max_iter, ctypes.byref(nb_iter), res_arr, ctypes.byref(ok))

Теперь myFun изменяет res_arr, который является массивом структуры, как видно из приведенного выше кода,Это точно

<__main__.myStruct_Array_10000 at 0x97966c8>)

после кода, показанного выше, но я не могу понять, как преобразовать его в массив NumPy для будущего использования эффективно .

Конечно, ямог бы делать циклы с такими вещами, как for field, _ in struct._fields_, как показано здесь , но это не главное, поскольку я использую DLL для ускорения вычислений (я действительно видел разницу во времени выполнения).res_arr имеет размер от 200 кб до 1 Мб и содержит десятки тысяч строк и несколько десятков столбцов, поэтому я уверен, что есть способ не проходить все это с помощью циклов, но я не могу понять,как это сделать красиво.

Кажется, что если бы это был не массив struct, это было бы проще.Есть a несколько SO вопросов (также здесь , здесь , здесь и здесь ) близко к этому предмету, но речь идет либо о преобразовании просто структуры, просто массива, или чего-то близкого, но никогда не похожего на меня, и мне не удалось приспособитьрешения, так что, возможно, есть способ основать ответ на этом, но в любом случае я весь слух.

1 Ответ

0 голосов
/ 07 сентября 2018

У нас почти такая же проблема, но в моем случае я использовал CUDA DLL, поэтому мой компилятор был nvcc.Но я считаю, что это также может быть сделано обычным g++ компилятором.В любом случае, вот шаги, которые я сделал, чтобы преобразовать массив структур из моего файла CPP в пригодный для использования список / массив Python.Я не буду проходить через ваш код;Вместо этого я приведу вам пример, который можно найти здесь: https://github.com/jcbacong/python-cpp.git

Но важные шаги сводятся к следующему:

  1. Создание файла .cppс необходимым заголовочным файлом, содержащим объявление extern "C" для функции.В моем файле .cpp я возвратил массив struct вместо возврата void.

  2. Создайте файл .dll с помощью вашего компилятора.Опять же, в моем случае это было nvcc.Пример кода, который я связал через свою учетную запись на github, был скомпилирован с использованием nvcc.

  3. В вашем файле .py:

    3.1 Создайте класс Python с ctypes.Structure, чтобы скопировать определение структуры в вашем файле .cpp / .h.

    3.2 Инициализируйте ваш ввод / вывод, используя argtype/restype.Поскольку моя функция .cpp возвращает массив struct, restype задается как ctypes.Pointer(<your Python Class(ctypes.Structure)>).

    3.3 Я преобразовал все входные данные в читаемые типы.После вызова функции в моем .py файле полученный массив структур (_results в примере) может быть преобразован в список Python с помощью (results = _results[:ARRAY_SIZE]).

Надеюсьэто помогает !!

...