cython
Emballage C ++
Recherche…
Emballage d'une DLL: C ++ en Cython en Python
Cela illustre un exemple non trivial de l’encapsulation d’un dll C ++ avec Cython. Il couvrira les principales étapes suivantes:
- Créez un exemple de DLL avec C ++ à l'aide de Visual Studio.
- Enveloppez la DLL avec Cython pour qu'il puisse être appelé en Python.
Il est supposé que Cython est installé et peut l’importer avec succès en Python.
Pour l'étape DLL, il est également supposé que vous êtes familiarisé avec la création d'une DLL dans Visual Studio.
L'exemple complet comprend la création des fichiers suivants:
-
complexFunLib.h
: fichier d'en-tête pour la source DLL C ++ -
complexFunLib.cpp
: fichier CPP pour la source DLL C ++ -
ccomplexFunLib.pxd
: fichier "en-tête" Cython -
complexFunLib.pyx
: fichier "wrapper" Cython -
setup.py
: fichier d'installation de Python pour créercomplexFunLib.pyd
avec Cython -
run.py
: Exemple de fichier Python qui importe la DLL compilée Cython
Source DLL C ++: complexFunLib.h
et complexFunLib.cpp
Ignorez ceci si vous avez déjà un fichier source d'en-tête et de DLL. Tout d'abord, nous créons la source C ++ à partir de laquelle la DLL sera compilée à l'aide de Visual Studio. Dans ce cas, nous souhaitons effectuer des calculs de tableau rapides avec la fonction exponentielle complexe. Les deux fonctions suivantes effectuent le calcul k*exp(ee)
sur les tableaux k
et ee
, où les résultats sont stockés dans res
. Il y a deux fonctions pour adapter à la fois la précision simple et double. Notez que ces exemples de fonctions utilisent OpenMP. Assurez-vous donc que OpenMP est activé dans les options Visual Studio du projet.
Fichier 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);
Fichier 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]);
}
}
Source Cython: ccomplexFunLib.pxd
et complexFunLib.pyx
Ensuite, nous créons les fichiers source Cython nécessaires pour envelopper la DLL C ++. Dans cette étape, nous faisons les hypothèses suivantes:
- Vous avez installé Cython
- Vous possédez une DLL de travail, par exemple celle décrite ci-dessus
Le but ultime est de créer un fichier .pyd
pouvant être importé en tant que module Python et exposant les fonctions écrites en C ++.
Fichier PXD
Ce fichier correspond au fichier d'en-tête C ++. Dans la plupart des cas, vous pouvez copier-coller l'en-tête sur ce fichier avec des modifications mineures spécifiques à Cython. Dans ce cas, les types complexes Cython spécifiques ont été utilisés. Notez l'ajout de c
au début de ccomplexFunLib.pxd
. Ce n'est pas nécessaire, mais nous avons constaté qu'une telle convention de nommage aide à maintenir l'organisation.
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);
Fichier PYX
Ce fichier correspond au fichier source cpp
C ++. Dans cet exemple, nous allons transmettre des pointeurs à des objets Numpy ndarray
aux fonctions d'importation de DLL. Il est également possible d'utiliser l'objet Cython memoryview
pour les tableaux, mais ses performances peuvent ne pas être aussi bonnes que ndarray
objets ndarray
(cependant, la syntaxe est nettement plus ndarray
).
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)
Source Python: setup.py
et run.py
setup.py
Ce fichier est un fichier Python qui exécute la compilation Cython. Son but est de générer le fichier .pyd
compilé qui peut ensuite être importé par les modules Python. Dans cet exemple, nous avons conservé tous les fichiers requis (ie complexFunLib.h
, complexFunLib.dll
, ccomplexFunLib.pxd
et complexFunLib.pyx
) dans le même répertoire que setup.py
.
Une fois ce fichier créé, il doit être exécuté à partir de la ligne de commande avec les paramètres suivants: build_ext --inplace
Une fois ce fichier exécuté, il doit générer un fichier .pyd
sans .pyd
aucune erreur. Notez que dans certains cas, s'il y a une erreur, le .pyd
peut être créé mais n'est pas valide. Assurez-vous qu'aucune erreur n'a été générée lors de l'exécution de setup.py
avant d'utiliser le .pyd
généré.
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
Maintenant, complexFunLib
peut être importé directement dans un module Python et les fonctions DLL encapsulées sont appelées.
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)