広告 プログラミング

【解説】DockerによるPythonコンパイラ「Codon」の環境構築方法

※本ページには、プロモーション(広告)が含まれています。

悩んでいる人
悩んでいる人

Pythonコンパイラ「Codon」をRaspberry Piで利用したい。

ただ、install.shではインストールに失敗するため、環境構築方法を教えて欲しい。

こんなお悩みを解決します。

先日、Pythonのコードをマシンコードにコンパイルするコンパイラ「Codon」が発表[論文]されました。

CodonによりPythonスクリプトをコンパイルして実行することで、通常のPythonよりも10倍から100倍速く実行でき、これはCやC++に匹敵します。

Pythonのコーディングのしやすさを保ちつつ、処理時間を短縮できるため、今後、注目すべきツールとなります。

今回は、Raspberry Pi上にDockerを用いたCodonの実行環境を構築する方法について解説します。

手元の環境ですぐに再現できるように、一通りファイルを用意しているので、興味がある方はぜひご覧ください。

Codonとは?

Codonは、公式サイトで以下のように説明されています。

Codon is a high-performance Python compiler that compiles Python code to native machine code without any runtime overhead. Typical speedups over Python are on the order of 100x or more, on a single thread. Codon supports native multithreading which can lead to speedups many times higher still.

https://docs.exaloop.io/codon/

ざっと訳すと以下のようになります。

Codonとは

Codonは、高パフォーマンスPythonコンパイラーで、実行時のオーバーヘッドなしにPythonコードをネイティブマシンコードにコンパイルする。

Pythonに対する一般的なスピードアップは、シングルスレッドで100倍以上のオーダーになる。

Codonは、ネイティブのマルチスレッドをサポートしており、さらに何倍ものスピードアップが可能となる。

実行時のオーバーヘッドがない点と通常でも100倍程度の高速化が期待できることが魅力的ですね。

一方、注意点として、以下の事が挙げられます。

  1. すべてのモジュールに対してサポートできている訳ではない。
  2. 一部の動的機能(動的型操作や実行時リフレクションなど)はサポートされていない。
    ※実行時リフレクション:プログラムの実行過程で、自身の構造を読み取ったり、書き換えたりすること。
  3. 2025年11月1日にApache Licence 2.0に移行する。それまでは、非商用で利用することを条件に、コピー、配布、改変を許可している。

実装方法によっては、上記の1.と2.に該当する可能性があるため、Pythonのまま実行するなど、対策が必要になります。

リポジトリ(GitHub)

これまでと同様に、今回も該当するコード一式をGitHubで管理しています。

先に全体像を確認したい方は、以下のリンクにアクセスしてください。

https://github.com/yuruto-free/codon-docker

環境構築

以降では、環境構築の方法について解説していきます。

ディレクトリ構成

今回、環境構築を行う際のディレクトリ構成は、以下のようになります。


./
|-- Dockerfile
|-- README.md
|-- docker-compose.yml
|-- start.sh
`-- src/
    `-- estimate_pi.py

また、テストコードとして、ライプニッツの公式を用いて円周率を推定するスクリプトを追加しています。

$$
\begin{align*}
& & 1 - \frac{1}{3} + \frac{1}{5} - \frac{1}{7} + \frac{1}{9} - \cdots = \frac{\pi}{4} \\
& \Leftrightarrow & \sum_{n = 0}^{\infty} \frac{(-1)^{n}}{2n + 1} = \frac{\pi}{4}
\end{align*}
$$

docker-compose.ymlの内容

ベースとなるdocker-compose.ymlの内容を以下に示します。

version: '3.7'

x-logging:
    &json-logging
    driver: json-file
    options:
        max-size: "1m"
        max-file: "3"

services:
    codon:
        build:
            context: .
            dockerfile: Dockerfile
            args:
                TZ: 'Asia/Tokyo'
        image: codon
        container_name: codon
        environment:
            - TZ=Asia/Tokyo
        volumes:
            - ./src:/code
        restart: "no"
        logging: *json-logging

Dockerfileの内容

環境構築の核となる、Dockerfileの内容を以下に示します。

FROM alpine:3.17
ARG TZ='Asia/Tokyo'

ENV PYTHONUNBUFFERED=1 \
    PYTHONIOENCODING=utf-8 \
    CODON_PYTHON=/usr/lib/libpython3.so \
    CODON_VERSION=0.15.5

LABEL maintainer="yuruto"

RUN    apk update \
    \
    # Install basic libraries and setup timezone
    \
    && apk add --no-cache bash tzdata gcc musl-dev zlib-dev zstd-libs \
    && cp /usr/share/zoneinfo/${TZ} /etc/localtime \
    && ln -s ${TZ} /etc/timezone \
    \
    # install temporary libraries
    \
    && apk add --no-cache --virtual .build-deps \
               libffi-dev g++ libgcc libstdc++ libxslt-dev python3-dev libc-dev linux-headers \
               openssl-dev curl cmake automake make llvm-dev llvm-static alpine-sdk \
    \
    # install python3 and pip3
    \
    && apk add --no-cache python3 \
    && python3 -m ensurepip \
    && rm -r /usr/lib/python*/ensurepip \
    && pip3 install --upgrade pip setuptools \
    \
    # create symbolic link
    \
    && ln -sf /usr/bin/python3 /usr/bin/python \
    && ln -sf /usr/bin/pip3 /usr/bin/pip \
    \
    # install codon
    \
    && curl -fsSL https://github.com/exaloop/codon/archive/refs/tags/v${CODON_VERSION}.tar.gz --output /tmp/v${CODON_VERSION}.tar.gz \
    && cd /tmp \
    && tar zxvf v${CODON_VERSION}.tar.gz \
    && cd codon-${CODON_VERSION} \
    && mkdir build \
    && cd build \
    && cmake .. \
    && make -j 4 \
    && make install \
    && cd / \
    && apk --purge del .build-deps \
    && mkdir /code \
    && rm -rf /root/.cache /var/cache/apk/* /tmp/*

# add shell script
COPY ./start.sh /start.sh
RUN chmod 777 /start.sh

WORKDIR /code

CMD ["/start.sh"]

今回、Raspberry PiでCodonの実行環境を作成するにあたり、バイナリファイルが見つからなかったため、ソースコードからビルドしています。

また、ソースコードからビルドする際にLLVMを利用しており、LLVMのバージョンの制約(Version 14以上)により、Alpineのバージョン3.17を利用しています。

start.shの内容

コンテナを維持しておくためのshell scriptを以下に示します。

#!/bin/bash

function handler() {
    echo "["$(date "+%Y/%m/%d-%H:%M:%S")"]" SIGTERM ACCEPTED
    exit 0
}

trap 'handler' TERM

while :; do
    sleep 3
done

動作確認用のテストコード

今回は、円周率の近似値を計算するPythonスクリプトを用意しました。

import time
from datetime import timedelta
from python import math

@python
def printf(data, _format):
    print(_format.format(data))

def Leibniz(max_iter : int):
    arr = reversed(range(max_iter))
    _sum = 0.0

    for k in arr:
        _sum += ((-1) ** (k % 2) + 0.0) / (2 * k + 1)
    pi = _sum * 4

    return pi

if __name__ == '__main__':
    max_iter = 1_000_000_000

    start_time = time.time()
    # estimate pi
    hat_pi = Leibniz(max_iter)
    elapsed_time = time.time() - start_time
    err = math.fabs(hat_pi - math.pi) / math.pi
    output = str(timedelta(seconds=elapsed_time))

    printf(hat_pi, 'Estimated Pi: {:.15f}')
    printf(err,    'Relative Err: {:.15e}')
    print(output)

環境構築方法

環境構築を行う際は、以下のコマンドを実行します。

# docker-compose.ymlとDockerfileがあるディレクトリで以下を実行する
docker-compose build --no-cache

ソースコードからビルドするため、イメージ作成まで時間を要することに注意してください。

使い方・動作確認

最後に、テストコードを用いて、Codonの使い方について解説します。

使い方としては、以下の2パターンがあります。

  • codon run(JITモード)
  • codon build(事前コンパイル)

上記は、事前コンパイルをするか、JITコンパイラを用いた実行時コンパイルを行うかという点が違いとして挙げられます。

実際に運用する際は、事前コンパイルしておくことになると思うので、codon buildの方を覚えておけば良いです。

それぞれの実行方法を以下に示します。

# ホスト環境での実行内容
docker exec -it codon bash

# Docker環境での実行内容
# JITモードでの実行
codon run estimate_pi.py
Estimated Pi: 3.141592652589793
Relative Err: 3.183097711628829e-10
0:00:27.889480

# 事前コンパイルによる実行
codon build --release estimate_pi.py
./estimate_pi
Estimated Pi: 3.141592652589793
Relative Err: 3.183097711628829e-10
0:00:08.967100

JITモードによる実行では、約28秒、事前コンパイル後は、約9秒となりました。

また、比較用にPythonコードをそのまま実行した結果も記載しておきます。

# スクリプトの一部を改変
cat <(echo "import math") <(grep -v "python" estimate_pi.py) > original.py
python original.py
Estimated Pi: 3.141592652589793
Relative Err: 3.183097711628829e-10
0:11:20.987000

結果は、約11分21秒(≒681秒)となりました。

これらをまとめると以下のようになります。

実行形式処理時間[sec]高速化率
pythonコード(オリジナル)680.9870001倍
JITモード27.889480約24倍
事前コンパイル8.967100約76倍
比較結果のサマリ

高速化率から分かるように、今回のケースでは最大76倍程度の高速化が期待できることが分かりました。

まとめ

今回は、Codonの環境構築方法とテストコードを用いた高速化の効果について解説しました。

通常のPythonコードが10倍程度~100倍程度、高速化できるため、積極的に利用していきたいツールであることが実感できたと思います。

興味がある方はぜひ利用してみてください。

スポンサードリンク



-プログラミング
-, , ,