Pythonを100倍の速度で実行する方法 ~Codonを試してみた~

Codonとは

 Codonというのは、新しくできたPythonコンパイラです。.pyファイルを機械語に翻訳することで、10倍から100倍の高速化を実現しています。
 ただ、全く同じように記述できるわけではなく、動的型付けができない・外部モジュールの利用が少し大変、などの問題点も存在します(後述)。それでも使い方を理解すれば強力な武器になることは間違いありませんので、ぜひ最後まで読んで実際に使ってみていただければと思います。

対応環境・インストール方法

 詳しいことはWelcome to Codon - Codonの公式リンクからいろいろ確認してください。記事作成現在では、MacLinuxでのみ利用可能となっています。筆者は主にWindowsでの開発をしているので、Windows上にwslを立ててその中にLinux版のCodonを入れました。
 以下のインストール方法はLinux版の方法になります。

/bin/bash -c "$(curl -fsSL https://exaloop.io/install.sh)"
echo 'export PATH="$HOME/.codon/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

上の3行を実行すると、インストールできてるはずです。

codon -version

で0.15.5のような数字(この数字は筆者環境)が出れば完了しています。

実際に実行&ベンチマーク

 

codon run -release main.py

の形で実行します。
 -lreleaseのオプションを付けることで、より最適化された形にコンパイルすることができます。
 バイナリファイルを生成したい場合には、

codon build -release main.py

で、mainという実行ファイルが生成され、

./main

で実行できます。
 実行ファイル名を指定したい場合には、

codon build -release main.py -o prime

のように、-oのオプション(gccとかと同じ)を付けることで指定できます。

 今回は、以前使った素数数え上げプログラム(番外編:サーバーを借りた話 - chachaの備忘録参照)を改良したものを使います。

import math
import time

def isPrime (x,l):
    if x%2==0:
        return False
    sqr=math.sqrt(x)

    for i in l:
        if x%i==0:
            return False
        if i>sqr:
            break
    return True
def main():
    try:
        l=list(capacity=1000000)
    except:
        l=list()
    l.append(2)
    start = time.perf_counter()

    ip=10000000
    for i in range(3,ip+1):
        if isPrime(i,l)==True:
            #print(i)
            l.append(i)


    print("end")
    print(time.perf_counter() - start)

if __name__=="__main__":
    main()

main関数の最初のtry部分は、codonでリスト長を指定するためのものと、pythonで指定せずにリストを用意するためのものになっています。
 実行環境としては、
   OS: windows10+wsl(ubuntu20.04TLS)
   CPU:Core i5-11600KF
   MEM:DDR4-3200 32GB
といった感じのPCを使います。
  Python3側はpython3.11.2(記事作成時最新)、Codon側はcodon0.15.5(記事作成時最新)で事前ビルドした上で実行していきます。

処理系 実行時間(秒)
python3 10.59195
codon 0.6533494

ともに5回実行してその平均を求めたものですが、言葉通り桁違いの性能ですね、codon。16倍のスピードで実行できてるみたいです。

外部ライブラリの使い方

 現在の状態でも十分使えるのですが、標準モジュール以外のモジュールが使えません(numpyとかpandasとか)。pipでインストールできるモジュールを使う方法をまとめます。
 外部モジュールを使うには、共有ライブラリというものと連携させる必要があります。まず、以下のコマンドでlibpython3.8.soといったようなファイルを探します。

find / -name "libpython*.so"

だいたい、

/usr/lib/x86_64-linux-gnu/libpython3.8.so
/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so

みたいな感じで出力されると思います。
その中のどれか(筆者は一番上のもの)をCODON_PYTHON環境変数に以下のコマンドで設定します。

export CODON_PYTHON=/usr/lib/x86_64-linux-gnu/libpython3.8.so

こうすることで、python3.8にインストールしたモジュールを利用することができます。
 利用方法なのですが、pythonで実行するときと少しimport文の書き方が変わってきます。

from python import numpy

のように、from pythonを付ける必要があります。
 codonからnumpyを呼び出す際のスピードも計ってみようと思い、以下のコードで速度を計ってみます。1行目、2行目は処理系に合わせてコメントアウトを付けたり外したりします。

from python import numpy as np#codon用
#import numpy as np#python用
import time

start = time.perf_counter()
l=np.ones(1000000)
ans=0
for i in l:
    ans+=i
print(ans)
print(time.perf_counter() - start)
処理系 実行時間(秒)
python3 0.07621
codon 1.31325

このようになりました。codonは毎回numpyを呼び出しているのか、codonのほうが遅いという結果になりました。
 今後numpyはcodonにネイティブ対応するとのことですが、今のところnumpyが必要な環境ではcodonを使う恩恵はあまり感じないのかなと思います。

注意点

 今回は起こりませんでしたが、codonが静的型付けをする以上、動的に型を変化させるようなコードを実行することはできません。また、複数の型を一つのリストに入れることもできないため、pythonの特徴ともいうべき、型を意識しないコーディングが不可能になっています。
 また、ライセンスがBusiness Source License 1.1であるため、非商用でのみ利用可能となっています(2025/11からは商用利用も可能なApache Licenseに移行)。そういった点もお気を付けください。

感想

 一言でいうと、めちゃくちゃ楽しいです。今後numpyとかpandasとかが普通に使えるようになり、商用利用も可能になったら一気に広まるんだろうなぁと思ってます。
 ただ、やはり型付けしっかりしないといけないのがめんどくさいなぁと(pythonユーザーの業)。まあコンパイルエラーでしっかりと教えてくれるので、それを読んで直せる人なら特に違和感もなく使っていけると思います。僕は今後使っていく(予定)です。
 では今日はこんなところで。ご質問やご意見があればコメントへお願いします。ではでは~

参考にさせていただいたリンク集

あなたのPythonを100倍高速にする技術 / Codon入門
【Codon】Pythonコンパイラを試してみた
Welcome to Codon - Codon