0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

IPython, Jupyter-lab で C native module を autoreload するメモ

Last updated at Posted at 2021-01-14

背景

ipython, jupyterlab での autoreload 便利ですよね.

ipython を起動しながら自作モジュールを修正した場合
https://qiita.com/Accent/items/f6bb4d4b7adf268662f4

でも, native module(C module)では使えません

C extension modules cannot be reloaded, and so cannot be autoreloaded.

:cry: :cry: :cry: :cry: :cry:

処理のかかる部分を高速化したい時や Vulkan みたいな API 叩きたいときにはどうしても native module が必要です.

そしてその開発を rapid にするために, ipython, jupyterlab でも native module の自動リロード(autoreload)したいです!

あとは, データロードとかに時間かかったりするようなものだと, 再度プロセス起動だと時間かかってしまうので, 計算ロジックだけリロードしたいという需要もあります.

方法

Posix 環境(e.g. Linux, macOS)を想定します. Linux, macOS で動作確認しました.

Python 側から呼ぶ native 関数は, 基本それ自体で処理は完結(state 状態などを持たない)とします.

とりあえずはいくらか wrap スクリプトを書いて,

  • 関数が呼ばれるごとに dlopen して dll をロード
  • dll 内の関数を呼び出す
  • dlclose で dll のインスタンスを手動で close する

の方法で対処します. Windows の場合も対応する Win32 API を使うようにすれば対応できるでしょう.

最近の python? ですと, native c module? の _ctypes(_ prefix がついている)に dlclose があるので, それを使います.
(macOS だといつの間にやら /usr/lib/libc.dylib などはなくなっているため, libc をロードして dlclose を呼ぶというのができない)

簡単な native module を作ります.

// mymod.c
double add(double a, double b)
{
  return a + b;
}
$ clang -shared -fPIC -o mymod.so mymod.c

対応する python module です.

# pymod.py
from ctypes import *
import _ctypes

def myadd(a, b):
    dll = cdll.LoadLibrary("./mymod.so")
    print(dll._handle)

    dll.add.argtypes = [c_double, c_double]
    dll.add.restype = c_double

    ret = dll.add(a, b)

    _ctypes.dlclose(dll._handle)

    return ret


# test
if __name__ == '__main__':
    ret = myadd(4.2, 5.3)
    print(ret)

ipython で動かしてみます.

$ ipython 
Python 3.9.1 | packaged by conda-forge | (default, Dec  9 2020, 01:07:47) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.19.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import pymod

In [2]: pymod.myadd(4.2, 5.3)
4630007152
Out[2]: 9.5

ここで, mymod.c を変え, mymod.so を作り直してみます.

double add(double a, double b)
{
  return 2.0 * a + b;
}

ipython で続けます.

In [3]: pymod.myadd(4.2, 5.3)
5184901120
Out[3]: 13.7

Voila! :tada:

いけました!

その他の方法

RPC や HTTP 通信(e.g. JSON-RPC, WebSocket)とかにして, python と native module でプロセス分離してプロセス間通信するという手もあります. ただコード量が増えてめんどいですよね.
(特に C++ などの native 側)

pybind11 module(numpy array を受け渡し) の場合

現状ぱぱっとできる方法はありません.

numpy のデータ構造(Python buffer protocol)を C/C++ world で自前でデコード/エンコードするなりが考えられますが, 面倒です.

python interface の API は固定して, pybind11 側(C++ 側)で C++ world で dlopen/dlclose するか, やはりリロードしたい部分は RPC なりで別プロセス化が楽でしょうか.

TODO

  • inotify あたりでファイルをウォッチする仕組みと組み合わせて, dll が更新された時のみ dll リロードを行うようにする
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?