ナンクル力学系

学んだ事を書き連ねていこう。

Posts Tagged ‘python/c

Numpy 配列の添字チェックをCのマクロに埋め込んでon/offする方法

leave a comment »

こんなものを作って見ましたよ,という話:

  • デバッグ時だけ添字チェック出来る
    • 添字チェックのon/off切り替えはマクロで一発
  • マクロを使う方のソースでは,デバッグをあまり意識しないで使える
    • ...って言うのは無理があるかもw

マクロのフルバージョンは http://gist.github.com/119216 で.

ソース

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

 #ifdef NUMPY_DEBUG
/* ========================================================== Debugging NumPy */
static void *NumPyDebug_CheckAndGetPtr(PyArrayObject *aobj,
                                       char *name, int ndim, int *ind)
{
  int i;
  void *dptr = PyArray_DATA(aobj);
  PyGILState_STATE gstate;

  if(ndim != PyArray_NDIM(aobj)){
    /* specified number of dimentions is not same of its of array */
    gstate = PyGILState_Ensure();/* get GIL to raise IndexError */
    PyErr_Format(PyExc_IndexError,
                 "degree of array is not correct. %s->nd = %d != ndim =%d\n",
                 name, (int)PyArray_NDIM(aobj), ndim);
    PyGILState_Release(gstate);
  }
  for(i=0; i<ndim; ++i){
    if(ind&#91;i&#93; >= PyArray_DIM(aobj,i)){
      /* index ind[i] is out of range */
      gstate = PyGILState_Ensure();/* get GIL to raise IndexError */
      PyErr_Format(PyExc_IndexError,
                   "array %s's %d-th index %d is out of bounds(=%d).\n",
                   name, i, ind[i], (int)PyArray_DIM(aobj,i));
      PyGILState_Release(gstate);
      break;
    }
    dptr += ind[i] * PyArray_STRIDE(aobj, i);
  }
  return dptr;
}
static void *NumPyDebug_CheckAndGetPtr2(PyArrayObject *aobj, char *name,
                                        int i, int j)
{
  int ind[] = {i,j};
  return NumPyDebug_CheckAndGetPtr(aobj, name, 2, ind);
}
static int NumPyDebug_CheckError(void)
{
  int return_val=0;
  PyGILState_STATE gstate;
  gstate = PyGILState_Ensure();/* get GIL to check IndexError */
  if(PyErr_Occurred()!=NULL){/* error is occured */
    return_val = -1;
  }
  PyGILState_Release(gstate);
  return return_val;
}
 #define NPD_IF_ERR_OCCURRED_DO(dothis) \
  if(NumPyDebug_CheckError()<0){ dothis; }
 #define NPD_CHECKANDGETPTR2(a,i,j) NumPyDebug_CheckAndGetPtr2(a,#a,i,j)
 #define Da2(a, i, j) (*((double *) NPD_CHECKANDGETPTR2(a, i, j)))

 #else
/* ============================================================== Using NumPy */
 #define NPD_IF_ERR_OCCURRED_DO(dothis)
 #define Da2(a, i, j) (*((double *) PyArray_GETPTR2(a, i, j)))
 #endif

static int _check_array(PyObject *array, char *array_name,
                        int ndim, int type, char *type_name)
{
  if ( PyArray_Check(array) != 1 ){
    PyErr_Format( PyExc_ValueError, "%s is not of type numpy array",
                  array_name);
    return -1;
  }
  if ( PyArray_NDIM(array) != ndim ){
    PyErr_Format( PyExc_ValueError, "%s is not %d-dim array",
                  array_name, ndim);
    return -1;
  }
  if ( PyArray_TYPE(array) != type ){
    PyErr_Format( PyExc_ValueError, "%s is not of type %s",
                  array_name, type_name);
    return -1;
  }
  return 0;
}

 #define CHECK_ARRAY(array, ndim, type)         \
  _check_array(array, #array, ndim, type, #type)

static PyObject *
mult2d(PyObject *dummy, PyObject *args)
{
  int i, j, *dims;
  PyObject *o1, *o2;
  PyArrayObject *a1, *a2, *a3;

  if (PyArg_ParseTuple(args, "OO:mult2d", &o1, &o2) < 0) {
    PyErr_SetString( PyExc_TypeError,  "bad arguments");
    return NULL;
  }
  if ( CHECK_ARRAY(o1, 2, NPY_DOUBLE) < 0 ){
    return NULL;
  }
  if ( CHECK_ARRAY(o2, 2, NPY_DOUBLE) < 0 ){
    return NULL;
  }
  a1 = (PyArrayObject *)o1;
  a2 = (PyArrayObject *)o2;

  dims = PyArray_DIMS(o1);
  a3 = (PyArrayObject *)PyArray_SimpleNew(2, dims, NPY_DOUBLE);
  if (a3 == NULL) {
    return NULL;
  }

  for (i = 0; i < dims&#91;0&#93;; ++i){
    for (j = 0; j < dims&#91;1&#93;; ++j){
      Da2(a3,i,j) = Da2(a1,i,j) * Da2(a2,i,j);
      /* check if error occured when the macro NUMPY_DEBUG is set */
      NPD_IF_ERR_OCCURRED_DO( Py_XDECREF(a3);
                              a3 = NULL;
                              goto _post_process )
    }
  }

 _post_process:
  /* some post process here */
  return (PyObject *)a3;
}

static PyMethodDef module_methods&#91;&#93; = {
  {"mult2d", (PyCFunction)mult2d, METH_VARARGS, "2d array multiplication"},
  {NULL}  /* Sentinel */
};

 #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
 #define PyMODINIT_FUNC void
 #endif
PyMODINIT_FUNC
initarraydebug(void)
{
  (void) Py_InitModule("arraydebug", module_methods);
  import_array();   /* required NumPy initialization */
}
&#91;/sourcecode&#93;</div>
</div>
<div id="outline-container-9.1.2">
<h4>setup.py</h4>
<div id="text-9.1.2">
from distutils.core import setup, Extension

module1 = Extension(
        'arraydebug',
        sources = ['arraydebugmodule.c']
)

setup(
        name = 'my',
        ext_modules = [module1],
)

コンパイル

  • 使う時
    • python setup.py build_ext -i -f
  • デバッグする時
    • python setup.py build_ext -i -f -DNUMPY_DEBUG

ちなみに, -i は–inplaceの略でその場に*.soを作る, -f はソースに変更が無くてもコンパイルする, -D[MACRO] は [MACRO] をCのマクロにセットする というオプション.

実行用のスクリプト:

import numpy
import arraydebug

a1 = numpy.arange( 10, dtype=float ).reshape(2,5)
a2 = a1
print "a1"
print a1
print "a2"
print a2
print

print "a3"
print arraydebug.mult2d(a1, a2)

print "---"
a2 = a2.reshape(5,2)

print "a1"
print a1
print "a2"
print a2
print

# トータルの要素数は同じなので,普通のコンパイルだとエラーは起きない
print "a3"
print arraydebug.mult2d(a1, a2)

普通にコンパイル (-DNUMPY_DEBUG なし) した時の実行結果

a1
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]]
a2
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]]

a3
[[  0.   1.   4.   9.  16.]
 [ 25.  36.  49.  64.  81.]]
---
a1
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]]
a2
[[ 0.  1.]
 [ 2.  3.]
 [ 4.  5.]
 [ 6.  7.]
 [ 8.  9.]]

a3
[[  0.   1.   4.   9.  16.]
 [ 10.  18.  28.  40.  54.]]

デバッグ時 (-DNUMPY_DEBUG あり) の実行結果

a1
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]]
a2
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]]

a3
[[  0.   1.   4.   9.  16.]
 [ 25.  36.  49.  64.  81.]]
---
a1
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]]
a2
[[ 0.  1.]
 [ 2.  3.]
 [ 4.  5.]
 [ 6.  7.]
 [ 8.  9.]]

a3
Traceback (most recent call last):
  File "mult2d.py", line 25, in <module>
    print arraydebug.mult2d(a1, a2)
IndexError: array a2's 1-th index 4 is out of bounds(=2).
Advertisements

Written by tkf

May 28, 2009 at 7:41 pm

Posted in PC

Tagged with ,

python の C 拡張をスレッドに対応させる方法 (with callback)

with 3 comments

python の C 拡張をスレッドに対応させつつ,時々は Python の callback 関数を呼びたい時は,グローバルインタプリタロック(GIL) を取得しないといけないですよ,という例.

基本的には 「8.1 スレッド状態 (thread state) とグローバルインタプリタロック (global interpreter lock)」 に書いてある方法.

こんなことをする:
Py_BEGIN_ALLOW_THREADS
...ブロックが起きるような何らかの I/O 操作...
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
...コールバック呼び出し...
PyGILState_Release(gstate);
Py_END_ALLOW_THREADS

コンパイルに必要なファイル:

callbackthreadmodule.c
 #include <Python.h>

static PyObject *
set_and_call_callback(PyObject *dummy, PyObject *args)
{
  PyObject *temp;
  PyObject *my_callback = NULL;
  PyObject *callback_arg;
  PyObject *callback_result;
  PyGILState_STATE gstate;
  int i;

  if (! PyArg_ParseTuple(args, "OO:set_callback", &temp, &callback_arg)) {
    return NULL;
  }
  if (!PyCallable_Check(temp)) {
    PyErr_SetString(PyExc_TypeError, "parameter must be callable");
    return NULL;
  }
  Py_XINCREF(temp);         /* 新たなコールバックへの参照を追加 */
  Py_XDECREF(my_callback);  /* 以前のコールバックを捨てる */
  my_callback = temp;       /* 新たなコールバックを記憶 */

  Py_BEGIN_ALLOW_THREADS
  for(i=0; i<1000000000; ++i){
    if( !(i % 200000000) ){
      printf("i=%d\n", i);
      gstate = PyGILState_Ensure();
      callback_result = PyEval_CallObject(my_callback, callback_arg);
      if (callback_result == NULL){
        return NULL; /* エラーを返す */
      }
      PyGILState_Release(gstate);
    }
  }
  Py_END_ALLOW_THREADS

  /* "None" を返す際の定型句 */
  Py_INCREF(Py_None);
  return Py_None;
}

static PyMethodDef module_methods&#91;&#93; = {
  {"set_and_call_callback", (PyCFunction)set_and_call_callback,
   METH_VARARGS, "set callback"},
  {NULL}  /* Sentinel */
};

 #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
 #define PyMODINIT_FUNC void
 #endif
PyMODINIT_FUNC
initcallbackthread(void)
{
  (void) Py_InitModule("callbackthread", module_methods);
}
&#91;/sourcecode&#93;</div>
</div>
<div id="outline-container-12.4.1.2" class="outline-5">
<h5>setup.py</h5>
<div id="text-12.4.1.2">
from distutils.core import setup, Extension

module1 = Extension(
        'callbackthread',
        sources = ['callbackthreadmodule.c']
)

setup(
        name = 'my',
        ext_modules = [module1],
)
コンパイル方法 (その場にcallbackthread.soを作る):

python setup.py config build build_ext –inplace

まずは, thread モジュールを使ってみた

test_thread.py
import callbackthread
import thread

x = (1,2,3)
y = dict(a=2,c=2)

def print_anything(a):
    print a

thread.start_new_thread(callbackthread.set_and_call_callback, (print_anything, (x,)))
thread.start_new_thread(callbackthread.set_and_call_callback, (print_anything, (y,)))
実行
% python test_thread.py
i=0
zsh: segmentation fault  python test_thread.py
% python test_thread.py
i=0
i=0
% python test_thread.py
i=0
i=0
zsh: segmentation fault  python test_thread.py
ん?

確率的に segmentation fault している... そして,時々しないけどなぜだか一回目しかループしてないし. 謎.よく分からなかったけど,threading モジュールだとうまくいった↓

threading モジュールだとうまく行く

test_threading.py
import callbackthread
import threading

x = (1,2,3)
y = dict(a=2,c=2)

def print_anything(a):
    print a

class TestThread(threading.Thread):
    def run(self):
        callbackthread.set_and_call_callback(print_anything, (x,))

TestThread().start()
TestThread().start()
結果
% python test_threading.py
i=0
(1, 2, 3)
i=0
(1, 2, 3)
i=200000000
(1, 2, 3)
i=200000000
(1, 2, 3)
i=400000000
(1, 2, 3)
i=400000000
(1, 2, 3)
i=600000000
(1, 2, 3)
i=600000000
(1, 2, 3)
i=800000000
(1, 2, 3)
i=800000000
(1, 2, 3)
うまくいった!

ちゃんと CPU 二つ使ってることも確かめられた!

どうやら, 「sh1.2 pyblosxom : pythonのthread/threadingモジュールを見てみた」 と同じ問題くさい. しかし segmentation fault って...

やりたかったことは:

test_functor.py
import callbackthread
import threading

x = (1,2,3)
y = dict(a=2,c=2)

class Func():
    i = 0
    def call_back(self, a):
        print "Func.i =",self.i
        print a
        self.i += 1

func = Func()

class TestThread(threading.Thread):
    def run(self):
        callbackthread.set_and_call_callback(func.call_back, (x,))

TestThread().start()
TestThread().start()
実行
% python test_functor.py
i=0
Func.i = 0
(1, 2, 3)
i=0
Func.i = 1
(1, 2, 3)
i=200000000
Func.i = 2
(1, 2, 3)
i=200000000
Func.i = 3
(1, 2, 3)
i=400000000
Func.i = 4
(1, 2, 3)
i=400000000
Func.i = 5
(1, 2, 3)
i=600000000
Func.i = 6
(1, 2, 3)
i=600000000
Func.i = 7
(1, 2, 3)
i=800000000
Func.i = 8
(1, 2, 3)
i=800000000
Func.i = 9
(1, 2, 3)
Func.i ってのを二つのスレッドが交互に書き換えてるのが分かる

python 側のオブジェクトを操作するタイミングを C で決めらる!

Written by tkf

May 11, 2009 at 9:05 pm

Posted in PC, programming

Tagged with ,

python の C 拡張から python の関数を呼ぶ方法の例

with one comment

まんま「1.6 C から Python 関数を呼び出す」にかかれている方法だけど.

C拡張, setup.py, テスト用コード↓

callbackmodule.c

 #include <Python.h>

static PyObject *my_callback = NULL;

static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
  PyObject *result = NULL;
  PyObject *temp;

  if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
    if (!PyCallable_Check(temp)) {
      PyErr_SetString(PyExc_TypeError, "parameter must be callable");
      return NULL;
    }
    Py_XINCREF(temp);         /* 新たなコールバックへの参照を追加 */
    Py_XDECREF(my_callback);  /* 以前のコールバックを捨てる */
    my_callback = temp;       /* 新たなコールバックを記憶 */
    /* "None" を返す際の定型句 */
    Py_INCREF(Py_None);
    result = Py_None;
  }
  return result;
}

static PyObject *
my_call_callback(PyObject *dummy, PyObject *args)
{
  PyObject *callback_arg;
  PyObject *result;

  if (! PyArg_ParseTuple(args, "O:call_callback", &callback_arg)) {
    return NULL; /* PyArg_ParseTuple has raised an exception */
  }

  /* ここでコールバックを呼ぶ */
  result = PyEval_CallObject(my_callback, callback_arg);
  if (result == NULL){
    return NULL; /* エラーを返す */
  }
  /* 場合によってはここで結果を使うかもね */
  /* Py_DECREF(result); //これやったら,segmentation fault で落ちた */
  return result;
}

static PyObject *
my_call_callback2(PyObject *dummy, PyObject *args)
{
  PyObject *callback_arg;

  if (! PyArg_ParseTuple(args, "O:call_callback", &callback_arg)) {
    return NULL; /* PyArg_ParseTuple has raised an exception */
  }
  return PyEval_CallObject(my_callback, callback_arg);
}

static PyMethodDef module_methods[] = {
  {"my_set_callback", (PyCFunction)my_set_callback, METH_VARARGS, "set callback"},
  {"my_call_callback",(PyCFunction)my_call_callback, METH_VARARGS, "call callback"},
  {"my_call_callback2",(PyCFunction)my_call_callback2, METH_VARARGS, "call callback"},
  {NULL}  /* Sentinel */
};

 #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
 #define PyMODINIT_FUNC void
 #endif
PyMODINIT_FUNC
initcallback(void)
{
  (void) Py_InitModule("callback", module_methods);
}

メモ

初めに作ったのが my_call_callback で,かなりコードが絞れることが分かったので my_call_callback2 も書いてみた.やることは同じ.

setup.py

from distutils.core import setup, Extension

module1 = Extension(
        'callback',
        sources = ['callbackmodule.c']
)

setup(
        name = 'my',
        ext_modules = [module1],
)

test.py

import callback

x = (1,2,3)
y = dict(a=2,c=2)

callback.my_set_callback(list)
print callback.my_call_callback((x,))
print callback.my_call_callback((y,))

callback.my_set_callback(type)
print callback.my_call_callback((x,))
print callback.my_call_callback((y,))

callback.my_set_callback(list)
print callback.my_call_callback2((x,))
print callback.my_call_callback2((y,))

callback.my_set_callback(type)
print callback.my_call_callback2((x,))
print callback.my_call_callback2((y,))

コンパイル (その場にcallback.soを作る)

python setup.py config build build_ext –inplace

テスト:

% python test.py
[1, 2, 3]
['a', 'c']
<type 'tuple'>
<type 'dict'>
[1, 2, 3]
['a', 'c']
<type 'tuple'>
<type 'dict'>

Written by tkf

May 11, 2009 at 8:28 pm

Posted in PC, programming

Tagged with ,

PythonのC拡張でAPIを公開する例

leave a comment »

PythonのC拡張を普通に書くと、その関数などは他のC拡張モジュールから使えない。という訳で他のC拡張から読み込めるようにAPIを公開する必要がある訳だけど、その作り方が少しトリッキーだったので公開してみる。

参考:拡張モジュールに C API を提供すると、NumPyのソース。

(追記。そういえばPython/C拡張の練習で書いたコードがあったので晒す:http://snipplr.com/view/8215/example-of-numpyc-api/ C APIは使ってないけど。)

あんまりPythonのC拡張どころか、C言語もそんなに触って無いのでおかしい所があるかも。ツッコミ大歓迎です!

準備するのは以下のファイル。son.Sonとfather.Fatherというモジュールを作ってFatherのメンバにSonをもってきてみた。

Read the rest of this entry »

Written by tkf

September 18, 2008 at 10:42 pm

Posted in programming

Tagged with , ,

PythonのC拡張をその場でコンパイルする方法とicc使う方法

leave a comment »

setup.pyを書いて、次のコマンドを実行するだけ。

python setup.py config --compiler=intel build build_ext --inplace

「config –compiler=intel」でコンパイラ指定。 「build build_ext –inplace」でソースファイルがある場所にモジュール(modulename.soとか)作成。

追記:

「python setup.py build –help-compiler」で使えるコンパイラオプションの一覧が見れる。ググるよりも–helpで色々調べるのがいいかも。

Written by tkf

September 18, 2008 at 10:41 am

Posted in programming

Tagged with ,