14
17

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 1 year has passed since last update.

Codonを使いPythonで作成したプログラムをC/C++で活用する

Last updated at Posted at 2023-05-17

Pythonで作成したプログラムを、Codonを使いダイナミックリンクライブラリへコンパイルし、C/C++から呼び出す方法についてのメモ

Codonとは

Codonは高性能なPythonコンパイラです。実行時のオーバーヘッドなしにPythonコードをネイティブなマシンコードにコンパイルし、シングルスレッドで10-100倍以上の高速化が実現できます。Codonの開発はGithub上で行われており、2021年頃から現在まで様々な機能追加が行われています。

出典: あなたのPythonを100倍高速にする技術 / Codon入門
https://zenn.dev/turing_motors/articles/e23973714c3ecf

重要なのは、Pythonをネイティブなマシンコードにコンパイルができる点。

セットアップ

Codonのインストール
/bin/bash -c "$(curl -fsSL https://exaloop.io/install.sh)"

Pythonをコンパイルする

検証環境
macOS Monterey 12.6.3
pyenv 2.3.9
Python 3.9.7

次のプログラムをサンプルに用意した。

foo.py サンプルプログラム
def foo(n: int):
    for i in range(n):
        print(i * i)
    return n * n
foo(10)

コンパイルには、 codon build を使う。 -o オプションで出力ファイル名を指定できる

Pythonのコンパイル
codon build -o foo foo.py

出力した結果を file コマンドで確認すると、ちゃんとMacの実行可能ファイルになっている。
そして、実行もできる。

コンパイルした結果と実行
% file foo
foo: Mach-O 64-bit executable arm64
% ./foo
0
1
4
9
16
25
36
49
64
81

ダイナミックリンクライブラリの作成

Codonを使って、Pythonで書かれたコードからダイナミックリンクライブラリを作成する。
Webでオプションの一覧が見つからなかったので、 codon build --help の中から探す。

オプションの検索
% codon build --help | grep lib
    --lib                - Generate shared library
    -l <string>          - Link the specified library (only for executables)
    --libdevice=<string> - libdevice path for GPU kernels

--lib オプションをつける とライブラリを作成できるようだ。
ライブラリを作るにあたってPythonのプログラムの修正が必要である。
C/C++から呼びたい関数の上に @export と書かなければならない。
※ また、引数の型の定義が必要である。

foo.py ライブラリ用に修正したプログラム
@export
def foo(n: int):
    for i in range(n):
        print(i * i)
    return n * n

プログラムを修正したら以下のようにライブラリを作成する。

ダイナミックリンクライブラリの作成
codon build --lib -o libfoo.dylib foo.py

OSがMacなので、ダイナミックリンクライブラリの名前は、libXXX.dylib の形式が好ましい。
ちゃんとダイナミックリンクライブラリとして出力されるのが確認できる。

出力されたlibfoo.dylibの確認
% file libfoo.dylib
libfoo.dylib: Mach-O 64-bit dynamically linked shared library arm64

ダイナミックリンクライブラリのリンク

「ダイナミックリンクライブラリにコンパイルしたPythonプログラム」を呼び出す方も用意する。

call.c Pythonプログラムを呼び出す側
#include <stdint.h>
#include <stdio.h>

extern int64_t foo(int64_t);

int main() {
  foo(10);
}

extern int64_t foo(int64_t) のように、関数定義を用意する。
型の対応については公式ドキュメント参照。

あとは、通常のC/C++のコンパイルと同様にできる。

ダイナミックリンクライブラリのリンク
gcc -o call -L. -lfoo call.c

libfoo.dylib をリンクしてることは otool -L で確認できる。

% otool -L call
call:
        libfoo.dylib (compatibility version 0.0.0, current version 0.0.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.100.3)

実行

あとは通常のプログラム同様に実行できる。

callの実行
% ./call
0
1
4
9
16
25
36
49
64
81

モジュールを使う

Pythonといえば、便利なモジュールが豊富である。
Codonでは、書き方に修正こそいるが import で様々なライブラリを使うことができる。

Numpyを使ったPythonプログラムをコンパイルする。

num.py Numpyを使ったPythonプログラム
from python import numpy as np

@export
def numpy_test():
    a = np.array([[1, 2], [3, 4]])
    b = np.array([[5, 6], [7, 8]])
    c = a * b
    print(c)

numpyを含む部分はCodonによるコンパイルはされず、Pythonの共有ライブラリから動的に呼び出されます
出典: あなたのPythonを100倍高速にする技術 / Codon入門

import の前に from python を追加する のがポイント。

call_numpy.c Pythonプログラムを呼び出す側
#include <stdint.h>
#include <stdio.h>

extern void numpy_test();

int main() {
  numpy_test();
}

先程と同じように、コンパイルしてリンクする。

コンパイルとリンク
% codon build --lib -o libnum.dylib num.py
% gcc -o call_numpy -L. -lnum call_numpy.c

最後に、 libpython.dylib の場所を環境変数 CODON_PYTHON に記述 して実行できる。

call_numpyの実行
% CODON_PYTHON=~/.pyenv/versions/3.9.7/lib/lib/libpython3.9.dylib ./call_numpy
[[ 5 12]
 [21 32]]
% 

libpython.dylibの場所は、Pythonで調べられる。 参考

libpython.dylibの場所を調べる。
>>> from distutils.sysconfig import get_config_var
>>> print(get_config_var("LIBDIR"))
~/.pyenv/versions/3.9.7/lib

CODON_PYTHON の設定を忘れた場合、次のようなエラーが出る。

CODON_PYTHONの設定を忘れた場合
% ./call_numpy
CError: dlopen(libpython.dylib, 0x0002): tried: '/Users/xxxxxxxxxxxx/.codon/lib/libpython.dylib' (no such file), '/Users/xxxxxxxxxxxx/.codon/lib/codon/libpython.dylib' (no such file), 'libpython.dylib' (no such file), '/usr/local/lib/libpython.dylib' (no such file), '/usr/lib/libpython.dylib' (no such file), '/Users/xxxxxxxxxxxx/xxxxxxxxx/xxxxxxxxxxx/xxxxxxx/libpython.dylib' (no such file)

Raised from: std.internal.dlopen.dlopen.2:0
/Users/xxxxxxxxxxxx/.codon/lib/codon/stdlib/internal/dlopen.codon:31:9

Backtrace:
  [0x104c230f3] std.internal.dlopen.dlopen.2:0[str,int].240 at /Users/xxxxxxxxxxxx/.codon/lib/codon/stdlib/internal/dlopen.codon:31
  [0x104c26fd7] std.internal.python.setup_python:0[bool].654 at /Users/xxxxxxxxxxxx/.codon/lib/codon/stdlib/internal/python.codon
  [0x104c27067] std.internal.python.ensure_initialized:0[bool].664 at /Users/xxxxxxxxxxxx/.codon/lib/codon/stdlib/internal/python.codon:886
  [0x104c27ea7] pyobj._import:0[str].773 at /Users/xxxxxxxxxxxx/.codon/lib/codon/stdlib/internal/python.codon:881
  [0x104c298bb] main.0 at /Users/xxxxxxxxxxxx/xxxxxxxxx/xxxxxxxxxxx/xxxxxxx/num.py:1
zsh: abort      ./call_numpy

pyenvでPythonをインストールしている場合、 libpython.dylib がディレクトリに無いことがある。
その時は、 --enable-framework を有効にして、Pythonを再インストールする。

libpython.dylibを含めたPythonのインストール
env PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install -v 3.9.7

その他

@export を忘れた場合、シンボルが見つからなくなる。

@exportを忘れた場合のエラー
Undefined symbols for architecture arm64:
  "_foo", referenced from:
      _main in call-4aedb6.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

雑記

  • 公式ドキュメントのinteroperabilityの内容を補完して日本語でまとめたものではあるが、試したことはまとめておくとよいので。
  • Codonのコンパイルオプションはそれなりにあるが、すべてをまとめたドキュメントをまだ見つけられてないので、試せることはまだまだありそう。
  • C/C++側でPythonのオブジェクトを取り回す方法については、まだ未検証なので検証したい。

参考にしたサイト

https://docs.exaloop.io/codon/interoperability/cpp
https://zenn.dev/turing_motors/articles/e23973714c3ecf
https://chacha-py.hatenablog.com/entry/2023/04/01/052243

14
17
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
14
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?