cython
Inpackning av C ++
Sök…
Inpackning av en DLL: C ++ till Cython till Python
Detta visar ett icke-trivialt exempel på att lägga in en C ++ dll med Cython. Det kommer att täcka följande huvudsteg:
- Skapa ett exempel på DLL med C ++ med Visual Studio.
- Packa in DLL med Cython så att det kan kallas i Python.
Det antas att du har Cython installerat och kan importera den i Python.
För DLL-steget antas det också att du är bekant med att skapa en DLL i Visual Studio.
Det fullständiga exemplet inkluderar skapandet av följande filer:
-
complexFunLib.h
: Header-fil för C ++ DLL-källan -
complexFunLib.cpp
: CPP-fil för C ++ DLL-källan -
ccomplexFunLib.pxd
: Cython "header" -fil -
complexFunLib.pyx
: Cython "wrapper" -fil -
setup.py
: Python-installationsfil för att skapacomplexFunLib.pyd
med Cython -
run.py
: Exempel Python-fil som importerar den sammanställda, Cython-lindade DLL-filen
C ++ DLL Källa: complexFunLib.h
och complexFunLib.cpp
Hoppa över det här om du redan har en DLL- och rubrikfilfil. Först skapar vi C ++ -källan från vilken DLL kommer att sammanställas med Visual Studio. I det här fallet vill vi göra snabbmatrisberäkningar med den komplexa exponentiella funktionen. Följande två funktioner utför beräkningen k*exp(ee)
på matriser k
och ee
, där resultaten lagras i res
. Det finns två funktioner för både enkel och dubbel precision. Observera att dessa exempelfunktioner använder OpenMP, så se till att OpenMP är aktiverat i Visual Studio-alternativen för projektet.
H-fil
// 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-fil
#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
och complexFunLib.pyx
Därefter skapar vi de Cython-källfiler som är nödvändiga för att radera C ++ DLL. I detta steg gör vi följande antaganden:
- Du har installerat Cython
- Du har en fungerande DLL, t.ex. den som beskrivs ovan
Det slutliga målet är att skapa använda dessa Cython-källfiler i samband med den ursprungliga DLL-filen för att sammanställa en .pyd
fil som kan importeras som en Python-modul och avslöjar funktionerna skrivna i C ++.
PXD-fil
Den här filen motsvarar C ++ -huvudfilen. I de flesta fall kan du kopiera och klistra in rubriken till den här filen med mindre Cythonspecifika ändringar. I detta fall användes de specifika Cython-komplextyperna. Observera tillägget av c
i början av ccomplexFunLib.pxd
. Detta är inte nödvändigt, men vi har funnit att en sådan namnkonvention hjälper till att upprätthålla organisationen.
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-fil
Den här filen motsvarar C ++ cpp
källfilen. I det här exemplet kommer vi att överföra pekare till Numpy ndarray
objekt till import-DLL-funktionerna. Det är också möjligt att använda det inbyggda Cython memoryview
objektet för matriser, men dess prestanda kanske inte är lika bra som ndarray
objekt (men syntaxen är betydligt renare).
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-källa: setup.py
och run.py
setup.py
Den här filen är en Python-fil som kör Cython-sammanställningen. Syftet är att generera den sammanställda .pyd
filen som sedan kan importeras av Python-moduler. I det här exemplet har vi lagrat alla nödvändiga filer (dvs complexFunLib.h
, complexFunLib.dll
, ccomplexFunLib.pxd
och complexFunLib.pyx
) i samma katalog som setup.py
.
När denna fil har skapats bör den köras från kommandoraden med parametrar: build_ext --inplace
När denna fil körs bör den producera en .pyd
fil utan att ta upp några fel. Observera att i vissa fall om det finns ett misstag kan .pyd
skapas men är ogiltig. Se till att inga fel kastades i utförandet av setup.py
innan du använde den genererade .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
importeras direkt till en Python-modul och de inslagna DLL-funktionerna kallas.
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)