cython
Umschließen von C ++
Suche…
Umschließen einer DLL: C ++ in Cython in Python
Dies zeigt ein nicht triviales Beispiel für das Umschließen einer C ++ - DLL mit Cython. Es wird die folgenden Hauptschritte abdecken:
- Erstellen Sie eine Beispiel-DLL mit C ++, die Visual Studio verwendet.
- Wickeln Sie die DLL mit Cython, damit sie in Python aufgerufen werden kann.
Es wird davon ausgegangen, dass Sie Cython installiert haben und es erfolgreich in Python importieren können.
Für den DLL-Schritt wird außerdem davon ausgegangen, dass Sie mit dem Erstellen einer DLL in Visual Studio vertraut sind.
Das vollständige Beispiel umfasst die Erstellung der folgenden Dateien:
-
complexFunLib.h
: Header-Datei für die C ++ - DLL-Quelle -
complexFunLib.cpp
: CPP-Datei für die C ++ - DLL-Quelle -
ccomplexFunLib.pxd
: Cython "Header" -Datei -
complexFunLib.pyx
: Cython-Wrapper-Datei -
setup.py
: Python-Setup-Datei zum Erstellen voncomplexFunLib.pyd
mit Cython -
run.py
: Beispiel für eine Python-Datei, die die kompilierte, mit Cythonrun.py
DLL importiert
C ++ - DLL-Quelle: complexFunLib.h
und complexFunLib.cpp
Überspringen Sie dies, wenn Sie bereits eine DLL- und Header-Quelldatei haben. Zuerst erstellen wir die C ++ - Quelle, aus der die DLL mit Visual Studio kompiliert wird. In diesem Fall möchten wir schnelle Array-Berechnungen mit der komplexen Exponentialfunktion durchführen. Die folgenden zwei Funktionen führen die Berechnung k*exp(ee)
für die Arrays k
und ee
, wobei die Ergebnisse in res
gespeichert werden. Es gibt zwei Funktionen, um sowohl einfache als auch doppelte Genauigkeit zu ermöglichen. Beachten Sie, dass diese Beispielfunktionen OpenMP verwenden. Stellen Sie daher sicher, dass OpenMP in den Visual Studio-Optionen für das Projekt aktiviert ist.
H Datei
// 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-Datei
#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 Source: ccomplexFunLib.pxd
und complexFunLib.pyx
Als Nächstes erstellen wir die Cython-Quelldateien, die zum Einschließen der C ++ - DLL erforderlich sind. In diesem Schritt treffen wir folgende Annahmen:
- Sie haben Cython installiert
- Sie besitzen eine Arbeits-DLL, zB die oben beschriebene
Das ultimative Ziel besteht darin, diese Cython-Quelldateien in Verbindung mit der Original-DLL zu erstellen, um eine .pyd
Datei zu erstellen, die als Python-Modul importiert werden kann und die in C ++ geschriebenen Funktionen .pyd
macht.
PXD-Datei
Diese Datei entspricht der C ++ - Headerdatei. In den meisten Fällen können Sie die Kopfzeile mit geringfügigen Cython-spezifischen Änderungen kopieren und in diese Datei einfügen. In diesem Fall wurden die spezifischen Cython-Komplextypen verwendet. Beachten Sie das Hinzufügen von c
am Anfang von ccomplexFunLib.pxd
. Dies ist nicht notwendig, aber wir haben festgestellt, dass eine solche Namenskonvention zur Aufrechterhaltung der Organisation beiträgt.
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-Datei
Diese Datei entspricht der C ++ cpp
- Quelldatei. In diesem Beispiel übergeben wir Zeiger auf Numpy ndarray
Objekte an die Import-DLL-Funktionen. Es ist auch möglich, das eingebaute Cython- memoryview
Objekt für Arrays zu verwenden, seine Leistung ist jedoch möglicherweise nicht so gut wie bei ndarray
Objekten (die Syntax ist jedoch wesentlich sauberer).
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-Quelle: setup.py
und run.py
setup.py
Diese Datei ist eine Python-Datei, die die Cython-Kompilierung ausführt. Der Zweck besteht darin, die kompilierte .pyd
Datei zu generieren, die dann von Python-Modulen importiert werden kann. In diesem Beispiel haben wir alle erforderlichen Dateien (z. B. complexFunLib.h
, complexFunLib.dll
, ccomplexFunLib.pxd
und complexFunLib.pyx
) im gleichen Verzeichnis wie setup.py
.
Nachdem diese Datei erstellt wurde, sollte sie über die Befehlszeile mit den Parametern ausgeführt werden: build_ext --inplace
Nach dem .pyd
dieser Datei sollte eine .pyd
Datei erstellt werden, ohne dass Fehler auftreten. Beachten Sie, dass in einigen Fällen, wenn ein Fehler .pyd
die .pyd
kann, aber ungültig ist. setup.py
Sie sicher, dass bei der Ausführung von setup.py
keine Fehler setup.py
bevor Sie das generierte .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
Jetzt kann complexFunLib
direkt in ein Python-Modul importiert und die complexFunLib
DLL-Funktionen aufgerufen werden.
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)