cython
C ++ 래핑
수색…
DLL 래핑하기 : C ++에서 Cython to Python
이것은 Cython으로 C ++ dll을 래핑하는 예를 보여줍니다. 다음 주요 단계를 다룹니다.
- Visual Studio를 사용하여 C ++로 예제 DLL을 만듭니다.
- Cython으로 DLL을 감싸서 Python으로 호출 할 수 있도록하십시오.
Cython이 설치되어 있고 Python으로 성공적으로 가져올 수 있다고 가정합니다.
DLL 단계에서는 Visual Studio에서 DLL을 만드는 것에 익숙하다고 가정합니다.
전체 예제에는 다음 파일 생성이 포함됩니다.
-
complexFunLib.h
: C ++ DLL 소스의 헤더 파일 -
complexFunLib.cpp
: C ++ DLL 소스의 CPP 파일 -
ccomplexFunLib.pxd
: Cython "header"파일 -
complexFunLib.pyx
: Cython "래퍼"파일 -
setup.py
: Cython으로complexFunLib.pyd
를 만들기위한 파이썬 설정 파일 -
run.py
: 컴파일 된 Cython wrapped DLL을 가져 오는 예제 Python 파일
C ++ DLL 소스 : complexFunLib.h
및 complexFunLib.cpp
이미 DLL 및 헤더 소스 파일이있는 경우이 작업을 건너 뜁니다. 먼저 Visual Studio를 사용하여 DLL을 컴파일 할 C ++ 소스를 만듭니다. 이 경우 복잡한 지수 함수를 사용하여 빠른 배열 계산을 수행하려고합니다. 다음 두 함수는 배열 k
와 ee
에 대해 k*exp(ee)
를 계산합니다. 결과는 res
저장됩니다. 단 정밀도와 배정 밀도를 모두 수용하는 두 가지 기능이 있습니다. 이 예제 함수는 OpenMP를 사용하므로 OpenMP가 프로젝트의 Visual Studio 옵션에서 활성화되어 있는지 확인하십시오.
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);
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]);
}
}
Cython 소스 : ccomplexFunLib.pxd
및 complexFunLib.pyx
다음으로 C ++ DLL을 래핑하는 데 필요한 Cython 소스 파일을 만듭니다. 이 단계에서는 다음과 같은 가정을합니다.
- Cython을 설치했습니다.
- 위에서 설명한 것과 같이 작동하는 DLL을 가지고 있습니다.
궁극적 인 목표는 Python 모듈로 가져올 수있는 .pyd
파일을 컴파일하기 위해 원본 DLL과 함께 Cython 소스 파일을 사용하여 C ++로 작성된 함수를 노출하는 것입니다.
PXD 파일
이 파일은 C ++ 헤더 파일에 해당합니다. 대부분의 경우, 약간의 Cython 특정 변경 사항을 사용하여이 파일로 헤더를 복사하여 붙여 넣을 수 있습니다. 이 경우 특정 Cython 복합 유형이 사용되었습니다. ccomplexFunLib.pxd
시작 부분에 c
를 추가합니다. 이것은 필수는 아니지만 그러한 명명 규칙이 조직 유지에 도움이된다는 사실을 발견했습니다.
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 파일
이 파일은 C ++ cpp
소스 파일에 해당합니다. 이 예제에서는 Numpy ndarray
객체에 대한 포인터를 가져 오기 DLL 함수에 전달합니다. 배열에 Cython memoryview
객체를 사용할 수도 있지만, 성능은 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)
파이썬 소스 : setup.py
와 run.py
setup.py
이 파일은 Cython 컴파일을 실행하는 Python 파일입니다. 그 목적은 컴파일 된 .pyd
파일을 생성하여 파이썬 모듈로 가져올 수있게하는 것입니다. 이 예제에서는 setup.py
와 동일한 디렉토리에 필요한 모든 파일 (예 : complexFunLib.h
, complexFunLib.dll
, ccomplexFunLib.pxd
및 complexFunLib.pyx
)을 보관했습니다.
이 파일을 만든 후에는 명령 줄에서 다음 매개 변수를 사용하여 실행해야합니다. build_ext --inplace
이 파일이 실행되면 오류를 발생시키지 않고 .pyd
파일을 생성해야합니다. 경우에 따라 .pyd
가 실수로 생성되었지만 잘못되었을 수 있습니다. 생성 된 .pyd
를 사용하기 전에 setup.py
를 실행하는 동안 오류가 발생하지 않았는지 확인하십시오.
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
이제 complexFunLib
Python 모듈로 직접 가져 complexFunLib
랩된 DLL 함수를 호출 할 수 있습니다.
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)