cython
ラッピングC ++
サーチ…
DLLのラッピング:CythonからPythonへのC ++
これは、C ++ dllをCythonでラップする例を示しています。それは次の主要なステップをカバーします:
- 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の "ヘッダー"ファイル -
complexFunLib.pyx
:Cythonの "ラッパー"ファイル -
setup.py
complexFunLib.pyd
を作成するためのPythonセットアップファイル -
run.py
:コンパイル済みのCythonラップDLLをインポートするPythonファイルの例
C ++ DLLソース: complexFunLib.h
およびcomplexFunLib.cpp
すでにDLLとヘッダソースファイルがある場合は、これをスキップしてください。最初に、Visual Studioを使用してDLLをコンパイルするC ++ソースを作成します。この場合、複雑な指数関数を使用して高速配列計算を行いたいと考えています。次の2つの関数は、結果がres
格納されるk
とee
配列k
k*exp(ee)
の計算を実行します。単精度と倍精度の両方に対応する2つの関数があります。これらのサンプル関数は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モジュールとしてインポートしてC ++で書かれた関数を公開する.pyd
ファイルをコンパイルするために、これらのCythonソースファイルを元のDLL .pyd
使用することです。
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関数にndarray
ます。組み込みの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)
Pythonソース: setup.py
とrun.py
setup.py
このファイルは、Cythonのコンパイルを実行するPythonファイルです。その目的は、コンパイルされた.pyd
ファイルを生成して、Pythonモジュールによってインポートすることです。この例では、 setup.py
と同じディレクトリに必要なすべてのファイル( complexFunLib.h
、 complexFunLib.dll
、 ccomplexFunLib.pxd
、およびcomplexFunLib.pyx
)を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モジュールに直接インポートし、ラップされた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)