web-dev-qa-db-ja.com

Cythonでリスト/ディクテーションを行う慣用的な方法は?

私の問題:STLマップとベクターを使用して生のC++で大規模なデータセットを処理すると、Cythonを使用するよりもかなり高速になる(メモリフットプリントが小さい)ことがよくあります。

この速度ペナルティの一部はPythonリストとdictを使用することによるものであり、Cythonで邪魔にならないデータ構造を使用するためのいくつかのトリックがあるかもしれないと思います。たとえば、このページ(- http://wiki.cython.org/tutorials/numpy )は、ND配列のサイズとタイプを事前に定義することにより、Cythonでnumpy配列を非常に高速にする方法を示しています。

質問:リスト/ディクテーションで同様のことを行う方法はありますか?それらに含まれると予想される要素または(キー、値)ペアの大まかな数を述べることによって? つまり、Cythonでリスト/ディクテーションを(高速)データ構造に変換する慣用的な方法はありますか?

そうでない場合は、C++で記述し、Cythonインポートでラップする必要があると思います。

38
ramanujan

Cythonはテンプレートをサポートするようになり、一部のSTLコンテナーの宣言が付属しています。

http://docs.cython.org/src/userguide/wrapping_CPlusPlus.html#standard-library を参照してください

彼らが与える例は次のとおりです。

from libcpp.vector cimport vector

cdef vector[int] vect
cdef int i
for i in range(10):
    vect.Push_back(i)
for i in range(10):
    print vect[i]
34
Sam Hartsfield

PythonでC++と同様の操作を行うと、処理が遅くなることがよくあります。listdictは実際には非常にうまく実装されていますが、=を使用すると多くのオーバーヘッドが発生します。 Pythonオブジェクト。C++オブジェクトよりも抽象的で、実行時にさらに多くのルックアップが必要です。

ちなみに、std::vectorlistと非常によく似た方法で実装されています。ただし、std::mapは、実際には、サイズが大きくなるにつれて、多くの操作がdictよりも遅くなるように実装されています。それぞれの適切に大きな例の場合、dictは、std::mapよりも遅い定数係数を克服し、実際にはルックアップ、挿入などの操作をより速く実行します。

std::mapstd::vectorを使用したい場合、何もあなたを止めません。 Pythonに公開する場合は、自分でラップする必要があります。このラッピングが節約したいと思っていた時間のすべてまたは多くを費やしても、ショックを受けないでください。私はあなたのためにこれを自動化するツールを知りません。

オブジェクトの作成を詳細に制御するためのCAPI呼び出しがあります。 「少なくともこれだけ多くの要素を含むリストを作成する」と言うことはできますが、これによってリストの作成と入力の操作の全体的な複雑さが改善されるわけではありません。リストを変更しようとしても、後で変更されることはありません。

私の一般的なアドバイスは

  • 固定サイズの配列が必要な場合(リストのサイズを指定することについて話します)、実際にはnumpy配列のようなものが必要になる場合があります。

  • コード内の一般置換にlistよりもstd::vectorを使用することで、必要なスピードアップが得られるとは思えません。舞台裏で使用したい場合は、満足のいくサイズとスペースの改善が得られる可能性があります(もちろん、測定せずにはわかりません。;))。

  • dictは実際にその仕事を本当にうまくやっています。 Python std::mapに基づく)で使用するための新しい汎用タイプを導入しようとは絶対にしません。これは、多くの重要な操作に間に合うようにアルゴリズムの複雑さが悪化します。少なくともいくつかの実装— dictがすでに持っているいくつかの最適化をユーザーに任せます。

    std::mapのようにもう少し機能するものが必要な場合は、おそらくデータベースを使用します。これは通常、dictに保存したいもの(または、さらに言えば、listに保存したもの)が大きくなりすぎて、メモリに快適に保存できない場合に行うことです。 Python stdlibと利用可能な他のすべての主要データベースのドライバーにsqlite3があります。

30
Mike Graham

C++は、ベクトルとそれに含まれる要素の静的宣言のためだけでなく、テンプレート/ジェネリックを使用して、ベクトルにのみの要素が含まれるように指定するため、高速です。特定のタイプ、例えば3つの要素のタプルを持つベクトル。 Cythonはこの最後のことを行うことができず、些細なことではないように聞こえます-どういうわけか、コンパイル時に強制する必要があります(実行時のタイプチェックはPythonはすでに行っています)。 Cythonのリストから何かをポップすると、それがどのタイプであるかを事前に知る方法はなく、型付き変数に入れると、速度ではなくタイプチェックが追加されるだけです。これは、Pythonインタープリターであり、数値以外のタスクに対するCythonの最も重大な欠点であるように思われます。

これを手動で解決する方法は、python list/dict(またはおそらくstd :: vector)を特定のタイプの要素またはキーと値の組み合わせのcdefクラスでサブクラス化することです。テンプレートが生成しているコードと同じものに。Cythonコードで結果のクラスを使用する限り、それは改善を提供するはずです。

データベースまたは配列を使用すると、別の問題が解決されます。これは、任意のオブジェクト(ただし、特定のタイプ、できればcdefクラス)をコンテナーに配置するためです。

そして、std :: mapはdictと比較されるべきではありません。 std :: mapはバランスの取れたツリーであるため、ソートされた順序でキーを維持します。dictは別の問題を解決します。より良い比較は、dictとGoogleのハッシュテーブルです。

9
Andreas

標準の array モジュールをPythonの場合、これがCythonの設定に適しているかどうかを確認できます。 Cythonを使用したことはありません。

3

ネイティブPythonリスト/ディクテーションをC++マップ/ベクターの速度まで、またはそれに近い場所でさえも取得する方法はありません。割り当てや型宣言とは関係ありませんが、インタプリタに支払います。あなたが言及した例(numpy)はC拡張であり、まさにこの理由でCで書かれています。

0
Karl Guertin