Forum - Pythonから共有ライブラリを呼び出せない

Jump to navigation Jump to search
Overview > Programming Contest > 2022 > Pythonから共有ライブラリを呼び出せない
[#111]

ctypesを使ってCで書いた共有ライブラリを呼び出すことを試みているのですが、エラーを吐いてうまくいきません。

以下のような簡単なhello worldを出力するコードを書いて、nccを使ってコンパイルはできるのですが、実行時dlopenのところで共有ライブラリをロードできないようです。nccではなくgccにすると期待通りhello worldを表示してくれます。

nccのオプションが適切に指定されていないということでしょうか?

 $ cat hello.c
 #include <stdio.h>
 void print_hello()
 {
   printf("hello world\n");
 }
 $ cat hello.py
 import ctypes
 lib = ctypes.CDLL('libhello.so')
 lib.print_hello()
 $ ncc -fPIC -O2 -c hello.c
 $ ncc -fPIC -O2 -shared hello.o -o libhello.so
 $ mv libhello.so ~/lib   # LD_LIBRARY_PATHを~/libに設定しているので...
 $ python3 hello.py
 Traceback (most recent call last):
 File "/home/user0502/proj/hello/hello.py", line 2, in <module>
 lib = ctypes.CDLL('libhello.so')
 File "/usr/lib64/python3.9/ctypes/__init__.py", line 374, in __init__
 self._handle = _dlopen(self._name, mode)
 OSError: libhello.so: cannot open shared object file: No such file or directory
Posted by O.nakagawa on 15 December 2022 at 02:41.
Edited by O.nakagawa on 15 December 2022 at 02:43.

PythonではなくCのプログラムから呼び出した場合も同様に共有ライブラリのロードに失敗するようです。gccでコンパイルした場合はうまく動くのですが…。

 $ cat main.c
 #include <stdio.h>
 void print_hello();
 int main(){
   print_hello();
   return 0;
 }
 $ ncc -L ${HOME}/lib main.c -lhello
 $ ./a.out
 ./a.out: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
Posted by O.nakagawa on 15 December 2022 at 03:47.

お問い合わせいただきありがとうございます。ご質問の件について回答いたします。

Pythonから共有ライブラリをロードできない件になりますが、 nccはベクトルエンジン(VE)用のオブジェクト/バイナリを生成するコマンドになっています。 ホスト(VH)側で動作するPythonスクリプトからnccでコンパイルされた共有ライブラリを 直接ロードすることはできません。

PythonからVE用の共有ライブラリを実行する方法は2通りございます。 ご不便をおかけしますが、以下いずれかの方法をご検討お願い致します。

(1) PythonライブラリNLCPyを利用する方法

NLCPyはNumPy-likeなAPIをVE上で実行するPythonライブラリです。

https://sxauroratsubasa.sakura.ne.jp/documents/nlcpy/ja/index.html

NLCPyの機能を利用することで、VE向けにコンパイルされた共有ライブラリをロードして実行することが可能になります。

https://sxauroratsubasa.sakura.ne.jp/documents/nlcpy/ja/reference/jit/index.html

お使いのPython環境にpipコマンドからインストールすることが可能です。

 $ pip3 install nlcpy

以下、NLCPyからnccでコンパイルされた共有ライブラリを呼び出す簡易的なサンプルになります。

・ソースコード

 $ cat vehello.c
 #include <stdio.h>
 void print_hello(int x)
 {
     printf("hello world VE %d\n", x);
 }
 $ cat hello_nlcpy.py
 import nlcpy
 lib_vehello = nlcpy.jit.CustomVELibrary(path='./libvehello.so')
 print_hello = lib_vehello.get_function(
     'print_hello',
     args_type=(nlcpy.ve_types.int32,),
     ret_type=nlcpy.ve_types.void
 )
 print_hello(10, sync=True)

・コンパイル

 $ ncc -fpic -shared -o libvehello.so vehello.c

・実行

 $ source /opt/nec/ve/nlc/2.3.0/bin/nlcvars.sh
 $ python3 hello_nlcpy.py
 hello world VE 10

(2) VEの共有ライブラリを呼び出すVHの共有ライブラリを作成し、ctypesから呼び出す方法

VHで動作するプログラムからVEの関数を呼び出すC APIとして、VE Offloading framework(VEO)が提供されています。

https://sxauroratsubasa.sakura.ne.jp/documents/veos/en/aveo/index.html その他にCUDA-likeなAPIとして、VEDAが提供されています。

https://sxauroratsubasa.sakura.ne.jp/documents/veos/en/veda/index.html

これらの機能を利用してVHからVEの関数を呼び出す共有ライブラリを作成することで、ctypesから間接的にVEの関数を呼び出すことが可能になります。


以下、VEOを利用した場合の簡易的なサンプルになります。

VEDAを使ったサンプルについては割愛させていただきます。もしVEDAをご利用になる際は以下をご参照ください。

https://sxauroratsubasa.sakura.ne.jp/documents/veos/en/veda/md_GettingStarted.html

・ソースコード

 $ cat vhhello.c
 #include <ve_offload.h>
 void call_hello(int x)
 {
   struct veo_proc_handle *proc = veo_proc_create(0);
   uint64_t handle = veo_load_library(proc, "./libvehello.so");
   struct veo_thr_ctxt *ctx = veo_context_open(proc);
   struct veo_args *argp = veo_args_alloc();
   veo_args_set_i32(argp, 0, x);
   uint64_t id = veo_call_async_by_name(ctx, handle, "print_hello", argp);
   uint64_t retval;
   veo_call_wait_result(ctx, id, &retval);
   veo_args_free(argp);
   veo_context_close(ctx);
   veo_proc_destroy(proc);
 }
 $ cat hello_veo.py
 import ctypes
 lib = ctypes.CDLL('./libvhhello.so')
 lib.call_hello(10)

・コンパイル

 $ gcc -fpic -shared -o libvhhello.so vhhello.c -I/opt/nec/ve/veos/include -L/opt/nec/ve/veos/lib64 \
     -Wl,-rpath=/opt/nec/ve/veos/lib64 -lveo
 $ ncc -fpic -shared -o libvehello.so vehello.c

※ vehello.cは(1)でご紹介したコードと同じ内容になります。

・実行

 $ python hello_veo.py
 hello world VE 10

また、Cのプログラムから共有ライブラリのロードに失敗してしまう件につきましては、 環境変数VE_LD_LIBRARY_PATHに共有ライブラリのパスが指定されていないことが原因と推測します。 環境変数LD_LIBRARY_PATHはVHで動作するプログラムに対するパス指定になっています。 お手数をおかけしますが、VEで動作するプログラムに対してはVE_LD_LIBRARY_PATHを設定した上で実行をお願い致します。

Posted by NEC SDK team (forum administrator) on 15 December 2022 at 08:56.
Edited by NEC SDK team (forum administrator) on 15 December 2022 at 08:59.

早速、とても丁寧に解説をしていただき、ありがとうございます!

nccはVE用のオブジェクト/バイナリを生成するコンパイラということですね。基本事項を理解していませんでしたが、わかりました。Pythonから呼び出すやり方もサンプルコードも含めてご教示いただき、ありがとうございます。どちらかというとNLCPyを使う方がやりやすい印象を持ちました。が、仕様がいろいろありそうなのでドキュメントを見ながら検討してみます。

Cプログラムから呼び出す方は、環境変数VE_LD_LIBRARY_PATHの設定ですね。やってみたらあっさり動作しました。

 (2) VEの共有ライブラリを呼び出すVHの共有ライブラリを作成し、ctypesから呼び出す方法

こちらもサンプルコードの動作を確認しました。

ただ、

 (1) PythonライブラリNLCPyを利用する方法

こちらの方は、NLCPyのインストールができず、サンプルコードの動作を確認できていません。以下のようなエラーが出ます。なぜでしょうか?

 $ pip3 install nlcpy
 Defaulting to user installation because normal site-packages is not writeable
 ERROR: Could not find a version that satisfies the requirement nlcpy (from versions: none)
 ERROR: No matching distribution found for nlcpy
Posted by O.nakagawa on 15 December 2022 at 12:23.

それぞれ以下のコマンドの実行結果を教えていただけますでしょうか。

 $ python3 -V
 $ which python3
 $ which pip3
Posted by NEC SDK team (forum administrator) on 16 December 2022 at 00:27.

以下の通りです。

 $ python3 -V
 Python 3.9.13
 $ which python3
 /usr/bin/python3
 $ which pip3
 /usr/bin/pip3

よろしくお願い致します。

Posted by O.nakagawa on 16 December 2022 at 03:29.

ご不便をおかけして申し訳ございません。 ご利用いただいている環境のPythonは3.9.13ですが、NLCPyはPython 3.6, 3.7, 3.8に対応しております。 現在、環境の管理者にPython 3.8をインストールいただくよう進めておりますので、もうしばらくお待ちください。

Python 3.8が利用できるようになりましたら、NLCPyのインストール方法もあわせてご連絡致します。

Posted by NEC SDK team (forum administrator) on 16 December 2022 at 09:12.

早速ご連絡いただき、ありがとうございます。 承知しました。お手数おかけしますが、よろしくお願い致します。

Posted by O.nakagawa on 16 December 2022 at 11:40.

お待たせして申し訳ございません。
先ほど、システム管理者からPython3.8のインストールが完了したとの連絡をいただきました。
/usr/bin/python3.8というコマンドが追加されていると思います。

このままでもPython3.8をご利用いただくことは可能ですが、Pythonの仮想環境を利用するパッケージ管理方法をご紹介いたします。
Pythonの仮想環境を利用することで、システムにインストールされたPythonから隔離されたローカル環境で パッケージの管理を行うことが可能になります。

・/usr/bin/python3.8から仮想環境を構築

 $ /usr/bin/python3.8 -m venv <env_name>

仮想環境の名前<env_name>はご自由に名前を設定してください。
コマンド実行後、仮想環境の名前<env_name>に応じたディレクトリが作成されます。

・仮想環境のアクティベート

 $ source <env_name>/bin/activate
 $ which python3  # python3のコマンドパスが/usr/bin/python3からローカルパスに変更されていることを確認
 $ which pip3     # pip3のコマンドパスが/usr/bin/pip3からローカルパスに変更されていることを確認
 $ python3 -V     # Python3.8が設定されていることを確認 

アクティベート後は、通常通りpython3コマンドを利用いただくことが可能です。
仮想環境へのライブラリのインストールはpip3コマンドをご利用ください。

 $ pip3 install nlcpy 

・仮想環境のディアクティベート

 $ deactivate

deactivateコマンドを実行することで、仮想環境を有効化する前の状態に戻ります。
再度、仮想環境を有効にするためには、アクティベート手順を実施する必要があります。
マシンに再ログインした場合も、同様にアクティベートが必要になります。

以上です。よろしくお願いいたします。

Posted by NEC SDK team (forum administrator) on 19 December 2022 at 06:28.

お手数おかけしました。 venv環境にてnlcpyをインストールして、無事サンプルコードの動作確認ができました。 ありがとうございました!!

Posted by O.nakagawa on 20 December 2022 at 03:36.