cython
Wikkelen C ++
Zoeken…
Een DLL verpakken: C ++ naar Cython naar Python
Dit toont een niet-triviaal voorbeeld van het inpakken van een C ++ dll met Cython. Het omvat de volgende hoofdstappen:
- Maak een voorbeeld-DLL met C ++ met Visual Studio.
- Omwikkel de DLL met Cython zodat deze in Python kan worden aangeroepen.
Er wordt aangenomen dat Cython is geïnstalleerd en met succes kan worden geïmporteerd in Python.
Voor de DLL-stap wordt ook aangenomen dat u bekend bent met het maken van een DLL in Visual Studio.
Het volledige voorbeeld omvat het aanmaken van de volgende bestanden:
-
complexFunLib.h
: Koptekstbestand voor de C ++ DLL-bron -
complexFunLib.cpp
: CPP-bestand voor de C ++ DLL-bron -
ccomplexFunLib.pxd
: Cython "header" -bestand -
complexFunLib.pyx
: Cython "wrapper" -bestand -
setup.py
: Python-installatiebestand voor het maken vancomplexFunLib.pyd
met Cython -
run.py
: Voorbeeld van een Python-bestand dat de gecompileerde, door Cython verpakte DLL importeert
C ++ DLL Bron: complexFunLib.h
en complexFunLib.cpp
Sla dit over als u al een DLL- en header-bronbestand hebt. Eerst maken we de C ++ -bron waaruit de DLL wordt gecompileerd met Visual Studio. In dit geval willen we snelle matrixberekeningen uitvoeren met de complexe exponentiële functie. De volgende twee functies voeren de berekening k*exp(ee)
op arrays k
en ee
, waar de resultaten worden opgeslagen in res
. Er zijn twee functies voor zowel enkele als dubbele precisie. Merk op dat deze voorbeeldfuncties OpenMP gebruiken, dus zorg ervoor dat OpenMP is ingeschakeld in de Visual Studio-opties voor het project.
H Bestand
// 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);
CPP-bestand
#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]);
}
}
Cython Bron: ccomplexFunLib.pxd
en complexFunLib.pyx
Vervolgens maken we de Cython-bronbestanden die nodig zijn om de C ++ DLL te verpakken. In deze stap maken we de volgende veronderstellingen:
- U hebt Cython geïnstalleerd
- U beschikt over een werkende DLL, bijvoorbeeld degene die hierboven is beschreven
Het uiteindelijke doel is om deze Cython-bronbestanden te maken in combinatie met de originele DLL om een .pyd
bestand te compileren dat kan worden geïmporteerd als een Python-module en de functies onthult die zijn geschreven in C ++.
PXD-bestand
Dit bestand komt overeen met het C ++ header-bestand. In de meeste gevallen kunt u de koptekst naar dit bestand kopiëren en plakken met kleine Cython-specifieke wijzigingen. In dit geval werden de specifieke Cython-complextypen gebruikt. Let op de toevoeging van c
aan het begin van ccomplexFunLib.pxd
. Dit is niet nodig, maar we hebben ontdekt dat een dergelijke naamgevingsconventie helpt de organisatie te handhaven.
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);
PYX-bestand
Dit bestand komt overeen met het C ++ cpp
-bronbestand. In dit voorbeeld geven we verwijzingen naar ndarray
objecten door aan de import DLL-functies. Het is ook mogelijk om het ingebouwde memoryview
Cython te memoryview
voor arrays, maar de prestaties zijn mogelijk niet zo goed als die van ndarray
(de syntaxis is echter aanzienlijk schoner).
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)
Python Bron: setup.py
en run.py
setup.py
Dit bestand is een Python-bestand dat de Cython-compilatie uitvoert. Het doel is om het gecompileerde .pyd
bestand te genereren dat vervolgens door Python-modules kan worden geïmporteerd. In dit voorbeeld hebben we alle vereiste bestanden (dat wil zeggen complexFunLib.h
, complexFunLib.dll
, ccomplexFunLib.pxd
en complexFunLib.pyx
) in dezelfde map setup.py
als setup.py
.
Nadat dit bestand is gemaakt, moet het worden uitgevoerd vanaf de opdrachtregel met parameters: build_ext --inplace
Zodra dit bestand is uitgevoerd, zou het een .pyd
bestand moeten produceren zonder fouten te veroorzaken. Merk op dat in sommige gevallen de .pyd
kan worden aangemaakt, maar ongeldig is. Zorg ervoor dat er geen fouten zijn setup.py
bij de uitvoering van setup.py
voordat u de gegenereerde .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
Nu kan complexFunLib
direct in een Python-module worden geïmporteerd en de ingepakte DLL-functies worden aangeroepen.
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)