Szukaj…


Zawijanie biblioteki DLL: C ++ do Cython do Python

Pokazuje to nietrywialny przykład owijania biblioteki dll C ++ w Cython. Obejmuje on następujące główne kroki:

  • Utwórz przykładową bibliotekę DLL z C ++ za pomocą Visual Studio.
  • Zawiń bibliotekę DLL za pomocą Cython, aby można ją było wywoływać w Pythonie.

Zakłada się, że masz zainstalowany Cython i możesz go pomyślnie zaimportować do Pythona.

W kroku DLL zakłada się również, że znasz tworzenie biblioteki DLL w programie Visual Studio.

Pełny przykład obejmuje utworzenie następujących plików:

  1. complexFunLib.h : plik nagłówka dla źródła DLL C ++
  2. complexFunLib.cpp : plik CPP dla źródła DLL C ++
  3. ccomplexFunLib.pxd : Plik nagłówka Cython
  4. complexFunLib.pyx : Plik „otoki” Cytona
  5. setup.py : plik instalacyjny Pythona do tworzenia complexFunLib.pyd pomocą Cython
  6. run.py : Przykładowy plik Python, który importuje skompilowaną bibliotekę DLL owiniętą Cython

C ++ DLL Źródło: complexFunLib.h i complexFunLib.cpp

Pomiń to, jeśli masz już bibliotekę DLL i plik źródłowy nagłówka. Najpierw tworzymy źródło C ++, z którego DLL zostanie skompilowana za pomocą Visual Studio. W tym przypadku chcemy wykonać szybkie obliczenia tablicowe przy pomocy złożonej funkcji wykładniczej. Poniższe dwie funkcje wykonują obliczenia k*exp(ee) na tablicach k i ee , gdzie wyniki są przechowywane w res . Istnieją dwie funkcje umożliwiające dostosowanie zarówno pojedynczej, jak i podwójnej precyzji. Pamiętaj, że te przykładowe funkcje używają OpenMP, więc upewnij się, że OpenMP jest włączony w opcjach Visual Studio dla projektu.

Plik H.

// Avoids C++ name mangling with extern "C"
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)  
#include <complex>
#include <stdlib.h>

// Handles 64 bit complex numbers, i.e. two 32 bit (4 byte) floating point numbers
EXTERN_DLL_EXPORT void mp_mlt_exp_c4(std::complex<float>* k, 
                                     std::complex<float>* ee,
                                     int sz, 
                                     std::complex<float>* res, 
                                     int threads);

// Handles 128 bit complex numbers, i.e. two 64 bit (8 byte) floating point numbers
EXTERN_DLL_EXPORT void mp_mlt_exp_c8(std::complex<double>* k,                                       std::complex<double>* ee,
                                     int sz, 
                                     std::complex<double>* res, 
                                     int threads);

Plik CPP

#include "stdafx.h"
#include <stdio.h>
#include <omp.h>
#include "complexFunLib.h"

void mp_mlt_exp_c4(std::complex<float>* k,
                   std::complex<float>* ee,
                   int sz,
                   std::complex<float>* res,
                   int threads)
{
    // Use Open MP parallel directive for multiprocessing
    #pragma omp parallel num_threads(threads)
    {
        #pragma omp for
        for (int i = 0; i < sz; i++) res[i] = k[i] * exp(ee[i]);
    }
}

void mp_mlt_exp_c8(std::complex<double>* k,
                   std::complex<double>* ee,
                   int sz, std::complex<double>* res,
                   int threads)
{
    // Use Open MP parallel directive for multiprocessing
    #pragma omp parallel num_threads(threads)
    {
        #pragma omp for
        for (int i = 0; i < sz; i++) res[i] = k[i] * exp(ee[i]);
    }
}

Źródło Cython: ccomplexFunLib.pxd i complexFunLib.pyx

Następnie tworzymy pliki źródłowe Cython niezbędne do zawinięcia biblioteki DLL C ++. W tym kroku przyjmujemy następujące założenia:

  • Zainstalowałeś Cython
  • Posiadasz działającą bibliotekę DLL, np. Tę opisaną powyżej

Ostatecznym celem jest stworzenie użycia tych plików źródłowych Cython w połączeniu z oryginalną biblioteką DLL do skompilowania pliku .pyd , który można zaimportować jako moduł Pythona i udostępnić funkcje napisane w C ++.

Plik PXD

Ten plik odpowiada plikowi nagłówkowemu C ++. W większości przypadków możesz skopiować i wkleić nagłówek do tego pliku z niewielkimi zmianami specyficznymi dla Cython. W tym przypadku zastosowano określone typy kompleksów Cython. Zwróć uwagę na dodanie c na początku ccomplexFunLib.pxd . Nie jest to konieczne, ale stwierdziliśmy, że taka konwencja nazewnictwa pomaga utrzymać organizację.

cdef extern from "complexFunLib.h":
    void mp_mlt_exp_c4(float complex* k, float complex* ee, int sz,
                       float complex* res, int threads);
    void mp_mlt_exp_c8(double complex* k, double complex* ee, int sz,
                       double complex* res, int threads);

Plik PYX

Ten plik odpowiada plikowi źródłowemu C ++ cpp . W tym przykładzie przekażemy wskaźniki do obiektów Numpy ndarray do funkcji importowania DLL. Możliwe jest również użycie wbudowanego obiektu memoryview pamięci memoryview dla tablic, ale jego wydajność może nie być tak dobra jak obiektów ndarray (jednak składnia jest znacznie czystsza).

cimport ccomplexFunLib  # Import the pxd "header"
# Note for Numpy imports, the C import most come AFTER the Python import
import numpy as np  # Import the Python Numpy
cimport numpy as np  # Import the C Numpy

# Import some functionality from Python and the C stdlib
from cpython.pycapsule cimport *

# Python wrapper functions.
# Note that types can be delcared in the signature

def mp_exp_c4(np.ndarray[np.complex64_t, ndim=1] k,
              np.ndarray[np.complex64_t, ndim=1] ee,
              int sz,
              np.ndarray[np.complex64_t, ndim=1] res,
              int threads):
    '''
    TODO: Python docstring
    '''
    # Call the imported DLL functions on the parameters.
    # Notice that we are passing a pointer to the first element in each array
    ccomplexFunLib.mp_mlt_exp_c4(&k[0], &ee[0], sz, &res[0], threads)
    
def mp_exp_c8(np.ndarray[np.complex128_t, ndim=1] k,
              np.ndarray[np.complex128_t, ndim=1] ee,
              int sz,
              np.ndarray[np.complex128_t, ndim=1] res,
              int threads):
    '''
    TODO: Python docstring
    '''
    ccomplexFunLib.mp_mlt_exp_c8(&k[0], &ee[0], sz, &res[0], threads)

Źródło Python: setup.py i run.py

setup.py

Ten plik jest plikiem w języku Python, który wykonuje kompilację Cython. Jego celem jest wygenerowanie skompilowanego pliku .pyd , który następnie można zaimportować za pomocą modułów Python. W tym przykładzie wszystkie wymagane pliki (tj. complexFunLib.h , complexFunLib.dll , ccomplexFunLib.pxd i complexFunLib.pyx ) complexFunLib.pyx w tym samym katalogu, co setup.py .

Po utworzeniu tego pliku, należy go uruchomić z wiersza poleceń z parametrami: build_ext --inplace

Po uruchomieniu .pyd plik powinien wygenerować plik .pyd bez zgłaszania błędów. Pamiętaj, że w niektórych przypadkach, jeśli wystąpi błąd, .pyd może zostać utworzony, ale jest nieprawidłowy. Upewnij się, że nie zostały .pyd żadne błędy podczas wykonywania setup.py przed użyciem wygenerowanego .pyd .

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np

ext_modules = [
    Extension('complexFunLib',
              ['complexFunLib.pyx'],
              # Note here that the C++ language was specified
              # The default language is C
              language="c++",  
              libraries=['complexFunLib'],
              library_dirs=['.'])
    ]

setup(
    name = 'complexFunLib',
    cmdclass = {'build_ext': build_ext},
    ext_modules = ext_modules,
    include_dirs=[np.get_include()]  # This gets all the required Numpy core files
)

run.py

Teraz complexFunLib można importować bezpośrednio do modułu Python i wywoływać zawinięte funkcje DLL.

import complexFunLib
import numpy as np

# Create arrays of non-trivial complex numbers to be exponentiated,
# i.e. res = k*exp(ee)
k = np.ones(int(2.5e5), dtype='complex64')*1.1234 + np.complex64(1.1234j)
ee = np.ones(int(2.5e5), dtype='complex64')*1.1234 + np.complex64(1.1234j) 
sz = k.size  # Get size integer
res = np.zeros(int(2.5e5), dtype='complex64')  # Create array for results

# Call function
complexFunLib.mp_exp_c4(k, ee, sz, res, 8)  

# Print results
print(res)


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow