質問

私はしばらくの間、ピーク フィッティング ライブラリを作成することについて考えてきました。私は Python をよく知っており、最初はすべてを Python で実装する予定ですが、最終的にはいくつかのコア ルーチンをコンパイル済み言語で再実装する必要があるかもしれないと考えています。

IIRC は、Python の本来の役割の 1 つはプロトタイピング言語としてのものでしたが、Python は関数、ファンクター、オブジェクトを関数やメソッドに渡すことをかなり自由に許可していますが、C や Fortran などには同じことが当てはまらないのではないかと思います。

コンパイル言語にインターフェースする必要があると想定される関数/クラスの設計について何を知っておくべきですか?そして、これらの潜在的な問題のうち、cTypes、bgen、 スイグ, ブースト.Python, サイソン または Python SIP?

この特定のユースケース (フィッティング ライブラリ) では、ユーザーが数学関数 (ガウス関数、ローレンツ関数など) を Python 関数として定義できるようにして、コンパイルされたコード フィッティング ライブラリによって解釈できるようにすることを想像します。配列の受け渡しと戻りも重要です。

役に立ちましたか?

解決

最後に、私が実際に価値のある答えを与えることができる質問:)。

私は仕事 (光学測定技術の博士号) のために、f2py、boost.python、swig、cython、pyrex を調査しました。私は swig を広範囲に使用し、boost.python をいくつか使用し、pyrex と cython をよく使用しました。ctypesも使用しました。これが私の内訳です:

免責事項:これは私の個人的な経験です。私はこれらのプロジェクトには一切関与していません。

しゃがむ:C++ ではうまく動作しません。そうあるべきですが、Linux と Mac OS X では、リンク手順での名前のマングリングの問題が私にとって大きな頭痛の種でした。C コードがあり、それを Python に接続したい場合、それは良い解決策です。私は自分のニーズに合わせて GTS をラップし、基本的に接続できる C 共有ライブラリを作成する必要がありました。お勧めしません。

Cタイプ:私は ctypes を使用して libdc1394 (IEEE カメラ ライブラリ) ラッパーを作成しましたが、それは非常に単純な経験でした。コードは次の場所にあります https://launchpad.net/pydc1394. 。ヘッダーを Python コードに変換するのは大変な作業ですが、すべてが確実に動作します。これは、外部ライブラリとインターフェースをとりたい場合に良い方法です。Ctypes は Python の stdlib にも含まれているため、誰でもすぐにコードを使用できます。これは、Python の新しいライブラリをすぐに試してみるのにも良い方法です。外部ライブラリとのインターフェースとして推奨します。

ブースト.Python:とても楽しめる。Python で使用したい独自の C++ コードがすでにある場合は、これを実行してください。この方法で、C++ クラス構造を Python クラス構造に変換するのは非常に簡単です。Python で必要な C++ コードがある場合には、これをお勧めします。

パイレックス/サイソン: Pyrex ではなく Cython を使用してください。期間。Cython はより高度で、より楽しく使用できます。最近では、SWIG や Ctypes で行っていたことはすべて cython で行っています。Python コードの実行が遅すぎる場合にも、これは最良の方法です。このプロセスは本当に素晴らしいです:Python モジュールを cython モジュールに変換してビルドし、Python のままであるかのようにプロファイリングと最適化を続けます (ツールを変更する必要はありません)。その後、Python コードとできるだけ多く (または少なく) C コードを混合して適用できます。これは、アプリケーションの一部全体を C で書き直すよりもはるかに高速です。内側のループを書き換えるだけです。

タイミング:ctypes の呼び出しオーバーヘッドが最も高く (~700ns)、次に boost.python (322ns)、次に直接 swig (290ns) です。Cython は呼び出しオーバーヘッドが最も低く (124ns)、時間をかけた部分で最高のフィードバックが得られます (cProfile サポート!)。この数字は、対話型シェルから整数を返す簡単な関数を呼び出した私の箱からのものです。したがって、モジュールのインポートのオーバーヘッドには時間が計測されず、関数呼び出しのオーバーヘッドのみが計測されます。したがって、プロファイリングと cy​​thon を使用して Python コードを迅速に取得するのが最も簡単で生産的です。

まとめ:あなたの問題には Cython を使用してください ;)。この概要が一部の人にとって役立つことを願っています。残りの質問に喜んでお答えします。


編集:言及するのを忘れました:数値的な目的 (つまり、NumPy への接続) には Cython を使用します。彼らはそれをサポートしています (彼らは基本的にこの目的のために cython を開発しているため)。したがって、これはあなたの決定にさらに +1 を与える必要があります。

他のヒント

SWIG や SIP は使用したことがありませんが、Python ラッパーを作成するのは簡単です。 ブースト.python 非常に強力で、比較的使いやすいです。

C/C++ と Python の間で型を渡すための要件が​​明確ではありませんが、C++ 型を Python に公開するか、ジェネリックを使用することで簡単に行うことができます。 boost::python::オブジェクト C++ API への引数。コンバータを登録して、Python 型を C++ 型に、またはその逆に自動的に変換することもできます。

boost.python を使用する予定がある場合は、 チュートリアル 始めるのに良い場所です。

必要なものと似たものを実装しました。Python関数と画像を引数として受け入れるC ++関数があり、画像の各ピクセルにPython関数を適用します。

Image* unary(boost::python::object op, Image& im)
{
    Image* out = new Image(im.width(), im.height(), im.channels());
    for(unsigned int i=0; i<im.size(); i++)
    {
        (*out)[i] == extract<float>(op(im[i]));
    }
    return out;
}

この場合、Image は Python に公開される C++ オブジェクト (float ピクセルを含むイメージ) で、op は Python で定義された関数 (実際には __call__ 属性を持つ任意の Python オブジェクト) です。この関数は次のように使用できます (単項関数が、Image とload関数も含まれる呼び出されたイメージ内にあると仮定します)。

import image
im = image.load('somefile.tiff')
double_im = image.unary(lambda x: 2.0*x, im)

ブーストで配列を使用することに関しては、私は個人的にこれを行ったことはありませんが、ブーストを使用して配列を Python に公開する機能が利用可能であることは知っています - これ 役立つかもしれません。

コンパイル済みコードへの最終的な移行を計画する最善の方法は、パフォーマンスが重要な部分を単純な関数のモジュールとして、 機能的なスタイル (ステートレスで副作用なし)、基本的なデータ型を受け入れて返します。

これにより、Python プロトタイプ コードから最終的にコンパイルされたコードへの 1 対 1 のマッピングが提供され、次を使用できるようになります。 ctypes 簡単に頭痛を回避できます。

ピーク フィッティングでは、ほぼ確実に配列を使用する必要があります。これは状況を少し複雑にしますが、ctypes を使用すれば十分に実行可能です。

本当により複雑なデータ構造を使用したい場合、または渡された引数を変更したい場合は、 スイグ または Python の標準 C 拡張インターフェイス やりたいことを実行できますが、多少の手間がかかります。

あなたがしていることについては、こちらもチェックしてみてください ナムピー, これは、C にプッシュしたい作業の一部を実行するだけでなく、 Python と C の間でデータを行き来するための追加のヘルプ.

f2py (一部の numpy) は、C/Fortran の数値処理コードをラップするための、SWIG および boost.python のより簡単な代替手段です。

私の経験では、Python コードから C コードを呼び出す簡単な方法が 2 つあります。他にも方法はありますが、どれも面倒で冗長です。

1 つ目の最も簡単な方法は、一連の C コードを別個の共有ライブラリとしてコンパイルし、ctypes を使用してそのライブラリ内の関数を呼び出すことです。残念ながら、基本的なデータ型以外のものを渡すことは簡単ではありません。

2 番目に簡単な方法は、C で Python モジュールを作成し、そのモジュール内の関数を呼び出すことです。これらの C 関数には、何の手間もかけることなく、必要なものをすべて渡すことができます。また、ここで説明するように、これらの C 関数から Python 関数またはメソッドを呼び出すのは簡単です。 https://docs.python.org/extending/extending.html#calling-python-functions-from-c

私には、賢明な解説を提供できるほど SWIG に関する経験がありません。また、ctypes を介してカスタム Python オブジェクトを C 関数に渡したり、C で新しい Python クラスを定義したりすることは可能ですが、これらは面倒で冗長なので、上記の 2 つのアプローチのいずれかを採用することをお勧めします。

Python は、関数、ファンクター、オブジェクトを関数やメソッドに渡すことをかなり寛容に許可しますが、C や Fortran などには同じことが当てはまらないのではないかと思います。

C では、関数を引数として関数に渡すことはできませんが、関数と同様に優れた関数ポインタを渡すことはできます。

C と Python のコードを統合しようとするときにこれがどれだけ役立つかわかりませんが、1 つの誤解を解いておきたいと思います。

上記のツールに加えて、次のツールを使用することをお勧めします。 パイレックス (Python 拡張モジュールの作成用) または サイコ (Python の JIT コンパイラーとして)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top