수색…


DLL 래핑하기 : C ++에서 Cython to Python

이것은 Cython으로 C ++ dll을 래핑하는 예를 보여줍니다. 다음 주요 단계를 다룹니다.

  • Visual Studio를 사용하여 C ++로 예제 DLL을 만듭니다.
  • Cython으로 DLL을 감싸서 Python으로 호출 할 수 있도록하십시오.

Cython이 설치되어 있고 Python으로 성공적으로 가져올 수 있다고 가정합니다.

DLL 단계에서는 Visual Studio에서 DLL을 만드는 것에 익숙하다고 가정합니다.

전체 예제에는 다음 파일 생성이 포함됩니다.

  1. complexFunLib.h : C ++ DLL 소스의 헤더 파일
  2. complexFunLib.cpp : C ++ DLL 소스의 CPP 파일
  3. ccomplexFunLib.pxd : Cython "header"파일
  4. complexFunLib.pyx : Cython "래퍼"파일
  5. setup.py : Cython으로 complexFunLib.pyd 를 만들기위한 파이썬 설정 파일
  6. run.py : 컴파일 된 Cython wrapped DLL을 가져 오는 예제 Python 파일

C ++ DLL 소스 : complexFunLib.hcomplexFunLib.cpp

이미 DLL 및 헤더 소스 파일이있는 경우이 작업을 건너 뜁니다. 먼저 Visual Studio를 사용하여 DLL을 컴파일 할 C ++ 소스를 만듭니다. 이 경우 복잡한 지수 함수를 사용하여 빠른 배열 계산을 수행하려고합니다. 다음 두 함수는 배열 kee 에 대해 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.pxdcomplexFunLib.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.pyrun.py

setup.py

이 파일은 Cython 컴파일을 실행하는 Python 파일입니다. 그 목적은 컴파일 된 .pyd 파일을 생성하여 파이썬 모듈로 가져올 수있게하는 것입니다. 이 예제에서는 setup.py 와 동일한 디렉토리에 필요한 모든 파일 (예 : complexFunLib.h , complexFunLib.dll , ccomplexFunLib.pxdcomplexFunLib.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)


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow