8

私は最近試しPyPyましたが、このアプローチに興味をそそられました。Python用のC拡張機能がたくさんあります。これらはすべて、配列PyArray_DATA()のデータセクションへのポインターを取得するために使用されます。残念ながら、PyPyはモジュール内の配列にnumpy相当するものをエクスポートしていないようです。そのため、Webサイトの推奨事項に従って使用してみました。これにより、Pythonレベルへのポインターを取得するタスクがプッシュされます。numpypycpyextctypes

2つの方法があるようです。

import ctypes as C
p_t = C.POINTER(C.c_double)

def get_ptr_ctypes(x):
    return x.ctypes.data_as(p_t)

def get_ptr_array(x):
    return C.cast(x.__array_interface__['data'][0], p_t)

2番目のものだけがPyPyで機能するので、互換性のために選択は明確です。CPythonの場合、どちらも地獄のように遅く、私のアプリケーションの完全なボトルネックになります。このポインタを取得するための高速でポータブルな方法はありますか?PyArray_DATA()または、PyPy(おそらく文書化されていない)に相当するものはありますか?

4

2 に答える 2

4

私はまだ完全に満足のいく解決策を見つけていませんが、それでも、CPythonではるかに少ないオーバーヘッドでポインターを取得するためにできることがあります。まず、上記の両方の方法が非常に遅い理由は、.ctypes.__array_interface__がオンデマンド属性であり、によって設定されarray_ctypes_get()array_interface_get()で設定されるためnumpy/numpy/core/src/multiarray/getset.cです。1つ目はctypesをインポートしてnumpy.core._internal._ctypesインスタンスを作成し、2つ目は新しいディクショナリを作成してデータポインタに加えて不要なものを大量に追加します。

このオーバーヘッドについてPythonレベルでできることは何もありませんが、ほとんどのオーバーヘッドをバイパスするCレベルでマイクロモジュールを作成できます。

#include <Python.h>
#include <numpy/arrayobject.h>

PyObject *_get_ptr(PyObject *self, PyObject *obj) {
    return PyLong_FromVoidPtr(PyArray_DATA(obj));
}

static PyMethodDef methods[] = {
    {"_get_ptr", _get_ptr, METH_O, "Wrapper to PyArray_DATA()"},
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC initaccel(void) {
    Py_InitModule("accel", methods);
}

の拡張機能として通常どおりコンパイルし、としてsetup.pyインポートします

try:
    from accel import _get_ptr
    def get_ptr(x):
        return C.cast(_get_ptr(x), p_t)
except ImportError:
    get_ptr = get_ptr_array

PyPyでは、from accel import _get_ptr失敗してget_ptrにフォールバックしget_ptr_arrayます。これはNumpypyで機能します。

パフォーマンスに関する限り、軽量のC関数呼び出しのctypes + accel._get_ptr()場合、基本的にオーバーヘッドがないネイティブのCPython拡張機能よりもかなり低速です。もちろん、これよりもはるかに高速でget_ptr_ctypes()あるget_ptr_array()ため、中程度の重みのC関数呼び出しではオーバーヘッドがわずかになる可能性があります。

PyPyとの互換性が得られましたが、科学計算アプリケーションでPyPyを評価するためにかなりの時間を費やした後、彼らが(かなり頑固に)サポートを拒否する限り、PyPyの将来は見えません。完全なCPythonAPI。

アップデート

ctypes.cast()を導入した後、それがボトルネックになっていることがわかりましたaccel._get_ptr()。インターフェイス内のすべてのポインタをとして宣言することで、キャストを取り除くことができますctypes.c_void_p。これは私が最終的に得たものです:

def get_ptr_ctypes2(x):
    return x.ctypes._data

def get_ptr_array(x):
    return x.__array_interface__['data'][0]

try:
    from accel import _get_ptr as get_ptr
except ImportError:
    get_ptr = get_ptr_array

ここでget_ptr_ctypes2()は、hiddenndarray.ctypes._data属性に直接アクセスしてキャストを回避します。PythonからヘビーウェイトとライトウェイトのC関数を呼び出すタイミングの結果は次のとおりです。

                             heavy C (few calls)      light C (many calls)
ctypes + get_ptr_ctypes():         0.71 s                   15.40 s
ctypes + get_ptr_ctypes2():        0.68 s                   13.30 s
ctypes + get_ptr_array():          0.65 s                   11.50 s
ctypes + accel._get_ptr():         0.63 s                    9.47 s

native CPython:                    0.62 s                    8.54 s
Cython (no decorators):            0.64 s                    9.96 s

したがって、 sがある場合accel._get_ptr()とないctypes.cast()場合、ctypesの速度は実際にはネイティブのCPython拡張機能と競合します。だから私は誰かが書き直すまで待つ必要がありますh5pymatplotlibそしてscipyctypesで何か深刻なことのためにPyPyを試すことができるようになります...

于 2013-03-29T10:07:31.993 に答える
0

それは十分な答えではないかもしれませんが、うまくいけば良いヒントです。コードの一部でscipy.weave.inline()を使用しています。私が実行する関数は非常に重く、いくつかのポインター/配列のみに依存しているため、インターフェイス自体の速度についてはよくわかりませんが、私には速いようです。たぶん、scipy.weaveコードから、特にからインスピレーションを得ることができますattempt_function_call

https://github.com/scipy/scipy/blob/master/scipy/weave/inline_tools.py#L390

scipy.weaveによって生成されるC++コードを確認したい場合は、

  1. ここから簡単な例を作成します:http://docs.scipy.org/doc/scipy/reference/tutorial/weave.html

  2. Pythonスクリプトを実行します

  3. scipy.weaveキャッシュフォルダーを取得します。

    import scipy.weave.catalog as ctl
    ctl.default_dir()
    Out[5]: '/home/user/.python27_compiled'
    
  4. フォルダ内に生成されたC++コードを見てください
于 2013-03-28T09:47:27.440 に答える