cython
Envolviendo C ++
Buscar..
Envolviendo un DLL: C ++ a Cython a Python
Esto demuestra un ejemplo no trivial de envolver una dll de C ++ con Cython. Cubrirá los siguientes pasos principales:
- Crea un ejemplo de DLL con C ++ usando Visual Studio.
- Envuelva la DLL con Cython para que pueda ser llamada en Python.
Se supone que tienes Cython instalado y puedes importarlo con éxito en Python.
Para el paso de DLL, también se asume que está familiarizado con la creación de una DLL en Visual Studio.
El ejemplo completo incluye la creación de los siguientes archivos:
-
complexFunLib.h
: archivo de encabezado para la fuente DLL de C ++ -
complexFunLib.cpp
: archivo CPP para la fuente DLL de C ++ -
ccomplexFunLib.pxd
: archivo "cabecera" de Cython -
complexFunLib.pyx
: archivo "contenedor" de Cython -
setup.py
: archivo de configuración de Python para crearcomplexFunLib.pyd
con Cython -
run.py
: Ejemplo de archivo Python que importa la DLL compilada y envuelta en Cython
C ++ DLL Fuente: complexFunLib.h
y complexFunLib.cpp
Salta esto si ya tienes una DLL y un archivo fuente de cabecera. Primero, creamos la fuente de C ++ a partir de la cual se compilará la DLL utilizando Visual Studio. En este caso, queremos hacer cálculos de matriz rápidos con la función exponencial compleja. Las dos funciones siguientes realizan el cálculo k*exp(ee)
en las matrices k
y ee
, donde los resultados se almacenan en res
. Hay dos funciones para acomodar tanto la precisión simple como la doble. Tenga en cuenta que estas funciones de ejemplo utilizan OpenMP, así que asegúrese de que OpenMP esté habilitado en las opciones de Visual Studio para el proyecto.
Archivo 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);
Archivo 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]);
}
}
Fuente de Cython: ccomplexFunLib.pxd
y complexFunLib.pyx
A continuación, creamos los archivos de origen de Cython necesarios para envolver la DLL de C ++. En este paso, hacemos las siguientes suposiciones:
- Has instalado Cython
- Usted posee un archivo DLL de trabajo, por ejemplo, el descrito anteriormente
El objetivo final es crear el uso de estos archivos de origen de Cython junto con la DLL original para compilar un archivo .pyd
que puede importarse como un módulo de Python y exponer las funciones escritas en C ++.
Archivo PXD
Este archivo corresponde al archivo de cabecera de C ++. En la mayoría de los casos, puede copiar y pegar el encabezado en este archivo con cambios menores específicos de Cython. En este caso, se utilizaron los tipos específicos de complejo de Cython. Tenga en cuenta la adición de c
al principio de ccomplexFunLib.pxd
. Esto no es necesario, pero hemos encontrado que tal convención de nombres ayuda a mantener la organización.
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);
Archivo PYX
Este archivo corresponde al archivo fuente de C ++ cpp
. En este ejemplo, pasaremos punteros a los objetos ndarray ndarray
a las funciones DLL de importación. También es posible utilizar el objeto incorporado en Cython memoryview
para arrays, pero su rendimiento puede no ser tan bueno como los objetos ndarray
(sin embargo, la sintaxis es significativamente más limpia).
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)
Fuente de Python: setup.py
y run.py
setup.py
Este archivo es un archivo de Python que ejecuta la compilación de Cython. Su propósito es generar el archivo .pyd
compilado que luego puede ser importado por los módulos de Python. En este ejemplo, hemos mantenido todos los archivos necesarios (es decir complexFunLib.h
, complexFunLib.dll
, ccomplexFunLib.pxd
y complexFunLib.pyx
) en el mismo directorio que setup.py
.
Una vez que se crea este archivo, debe ejecutarse desde la línea de comandos con los parámetros: build_ext --inplace
Una vez que se ejecuta este archivo, debe producir un archivo .pyd
sin generar ningún error. Tenga en cuenta que en algunos casos, si hay un error, se puede crear .pyd
pero no es válido. Asegúrese de que no se hayan generado errores en la ejecución de setup.py
antes de usar el .pyd
generado.
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
Ahora complexFunLib
puede ser importado directamente a un módulo de Python y las funciones DLL envueltas son llamadas.
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)