Python Monthly Topics

PythonとRustの融合⁠PyO3/maturinを使ったPythonバインディングの作成入門

門脇@satoru_kadowakiです。今月のPython Monthly Topicsでは、PythonとRustの融合を可能にするPyO3maturinについて紹介します。

はじめに

PythonRustはそれぞれ異なる特性を持つプログラミング言語です。Pythonはシンプルな構文で初学者にも親しみやすく、データサイエンス、Web開発など高レイヤーのライブラリ群が充実しています。しかし、パフォーマンスが要求される部分ではCやRustに比べて劣ることがあります[1]

一方、Rustはメモリやスレッドの安全性に重点を置いて設計されており、CPUやメモリなどの低レイヤーの処理効率に優れています。プログラムを書くこと自体が難しいとされている低レイヤーの処理を、パフォーマンスを損なわず書くことができる言語として広く利用されるようになりました。

2つの言語の特徴を生かし、Pythonでパフォーマンスが要求される処理を全てRustで置き換えることができれば性能向上が期待できますが、それにはRustの学習コストもあり大変です。

全てのプログラムをRust化するのは難しくとも、場合によっては「ある特定の処理だけでもパフォーマンスを改善したい」ということもあるかと思います。そこで本記事では、Pythonで書かれた簡単なプログラムの一部(関数)をRustで書き換え、maturinを使用してPythonバインディングとして呼び出す方法を紹介します。

Pythonにおけるその他の高速化方法

Pythonの高速化には、他にも以下のような手法が知られています。それぞれに特徴がありますが、今回紹介するmaturinはRustを使用した比較的新しい手法です。この機会にぜひ知っていただけたらと思います。

手法 概要
Cython Pythonライクなコードを、C言語に変換することで高速化を行う言語
numba PythonやNumPyで書かれた数値計算コードを高速に実行するためのJITコンパイラ
pypy Pythonの実装の1つで RPython によるJITコンパイラによって高速化される。⁠一部のC拡張と互換性がないものもある)

PyO3とmaturinについて

本記事では、Rustで書かれたプログラムをPythonから呼び出して実行するために、PyO3とmaturinを使用します。それぞれ以下のような役割があります。

PyO3はRustとPythonの相互運用を実現するためのライブラリです。PyO3によってRustからPythonのオブジェクトや関数を呼び出す、またはその逆の操作も行えます。

maturinはPythonの拡張モジュールをビルドし、Rustのクレート(Crate)というパッケージ化を行うためのツールです。内部でPyO3を使用しており、RustのコードをPythonから利用できる形にビルドするためのインターフェースを提供します。

それぞれの関係を簡単な図で表すと以下のようになります。

システム開発におけるmaturinとPyO3の関係
システム開発におけるmaturinとPyO3の関係

このように、maturinによってビルドが簡単になり、開発者は複雑なビルドプロセスを気にすることなくRustとPythonを使用した開発に集中することができます。なお、PyO3とmaturinのGitHubリポジトリは、以下のようにどちらもPyO3配下で開発が進められています。

以下にGitHubのリンクも掲載しておきます。

最近ではPythonのデータ分析関連、バリデーションツール、暗号化ライブラリなど、さまざまなプロジェクトでPyO3やRustを使用している事例も増えてきており、PythonとRustがより親和性の高い言語になっていることが窺えます。

参考までに、PyO3を使用しているプロジェクトには以下があり、Pythonのサードパーティライブラリの高速化にRustの特性が生かされています。

インストール

Rustのコードをビルドできるようにするために、Rustとmaturinをインストールします。ビルドに必要な環境のセットアップは上記2つのみでPyO3自体のインストールは必要ありません。

rustupのインストール

まず最初に、Rustの開発環境をインストールします。インストールは、Rust公式のインストーラーrustupを使用します。

本記事では以下の環境にインストールを行っています。

  • OS:Ubuntu 20.04.5 LTS
  • Python:3.11.4

Linux、Unix系OS、macOSについては、Install Rust ページに記載されている手順で行います。Windowsなど、その他のOSについては Other Rust Installation Methods ページでインストーラーが提供されていますので、確認してみてください。

rustupをインストールすると以下のような機能が提供されます。

  • Rustのバージョン管理:特定のバージョンのインストールなどを行う
  • ツールチェーンの管理:コンパイルに必要なツールチェーンのインストールなどを行う
    • コンパイラー:rustc
    • パッケージマネージャー:Cargo
  • コンポーネントの管理:標準ライブラリなどの管理を行う

rustupのインストールは以下のコマンドで行います。

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

インストールを実行すると以下のようにインストール方法のオプション選択が表示されます。標準(default)インストールである1を入力してEnterキーを押下します。

インストールオプションの選択画面
Welcome to Rust!

〈省略〉

Current installation options:

   default host triple: x86_64-unknown-linux-gnu
     default toolchain: stable (default)
               profile: default
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>1

インストールが続行され、以下のように必要なモジュール等のダウンロードとインストールが行われます。

インストール完了時の様子
info: profile set to 'default'
info: default host triple is x86_64-unknown-linux-gnu
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: latest update on 2023-06-01, rust version 1.70.0 (90c541806 2023-05-31)
info: downloading component 'cargo'
  6.9 MiB /   6.9 MiB (100 %)   2.6 MiB/s in  3s ETA:  0s

〈省略〉

Rust is installed now. Great!

To get started you may need to restart your current shell.
This would reload your PATH environment variable to include
Cargo's bin directory ($HOME/.cargo/bin).

To configure your current shell, run:
source "$HOME/.cargo/env"

インストールが完了すると、環境変数を再読み込みするために、シェルのリスタートまたは$HOME/.cargo/envファイルの再読み込みが必要です。標準出力の結果に記載があるとおり、以下のコマンドで行います。

$ source "$HOME/.cargo/env"

ここで念のためにrustupコマンドが使用可能であることを確認します。バージョン確認として-Vオプションを設定しています。

$ rustup -V
rustup 1.26.0 (5af9b9484 2023-04-05)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.70.0 (90c541806 2023-05-31)`

maturinのインストール

続いてmaturinをpipコマンドでインストールします。

$ pip install maturin

以上でインストールは完了です。

Rust関数をPythonでバインディングする

ここからは、Pythonで作成した関数をRust化して実行する方法について説明します。

使用するPythonスクリプトのサンプル

以下のサンプルコードでは、count_chars()関数において引数で指定された文字列について英字、数値、その他の文字列ごとにカウントした結果を返します。このサンプルコード自体をPythonでさらに最適化する方法もあると思いますが、今回はこのスクリプトをRust化してみます。なお、このサンプルスクリプトは後述の説明でも使用しますので、example_base.pyとして保存しておきます。

example_base.py ―文字数カウントを行うPythonスクリプトのサンプル
# 文字列を引数として英字、数値、その他の文字列をカウントした結果を返す
def count_chars(text):
    # カウント用変数定義
    alphabet_count = 0  # 英字カウント用
    digit_count = 0  # 数値カウント用
    other_count = 0  # その他の文字カウント用

    for char in text:  # 文字列をループで繰り返し
        if char.isalpha() and char.isascii():  # アルファベットかどうか
            alphabet_count += 1
        elif char.isdigit():  # 数値かどうか
            digit_count += 1
        else:
            other_count += 1

    return alphabet_count, digit_count, other_count


if __name__ == "__main__":
    text = "Python Monthly Topics: 2023年7月"
    alphabet_count, digit_count, other_count = count_chars(text)
    print(f"アルファベットの数: {alphabet_count}")
    print(f"数字の数: {digit_count}")
    print(f"それ以外の文字数: {other_count}")

このサンプルスクリプトの実行結果は以下の通りです。

$ python example_base.py
アルファベットの数: 19
数字の数: 5
それ以外の文字数: 6

maturinで最初のステップ

最初にPython拡張モジュールにするためのディレクトリを作成します。文字列カウントのスクリプトですので、ディレクトリ名をstrcounterとしました。作成後、strcounterディレクトリに移動します。

$ mkdir strcounter
$ cd strcounter

ディレクトリに移動後、以下のようにmaturin initコマンドを実行してRustのCargoプロジェクトを作成します(CargoプロジェクトとはCargoによって管理されるRustのソフトウェアプロジェクトです⁠⁠。

コマンドを実行するとバインディングの種類選択が要求されます。先頭にある「pyo3」を選択した状態でEnterキーを押下します。

maturin initコマンドの実行結果
(strcounter)$ maturin init
? 🤷 Which kind of bindings to use?
  📖 Documentation: https://maturin.rs/bindings.html ›
❯ pyo3
  rust-cpython
  cffi
  uniffi
  bin

✔ 🤷 Which kind of bindings to use?
  📖 Documentation: https://maturin.rs/bindings.html · pyo3
  ✨ Done! Initialized project /home/ubuntu/..(省略)../strcounter

maturin initコマンドで表示されたバインディングの種類からもわかりますが、PyO3以外のバインディングを選択することもできます。その他のバインディングについては、maturinのドキュメント - Bindingsに説明がありますので、興味のある方は確認してみてください。

さて、strcounterディレクトリを見てみると、以下のようなファイルやディレクトリが作成されています(下記ではPythonで記載されたサンプルコードとの位置関係をわかりやすくするために、example_base.pyを含めて表示しています⁠⁠。

$ tree -a
.
├── example_base.py  # pythonのサンプルスクリプト
├── strcounter
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── pyproject.toml
│   ├── src
│   │   └── lib.rs
│   ├── .github
│   │   └── workflows
│           └── CI.yml

作成された主なファイルの概要は以下の通りです。

ファイル名 概要
Cargo.toml Rustのビルドツールであるcargoの定義ファイル
pyproject.toml Pythonパッケージのビルドに必要な情報の定義ファイル
src/lib.rs Pythonバインディング用のscaffold(足場)
.github/workflows/CI.yml GitHub Actions用ワークフロー定義ファイル

Cargo.tomlにはデフォルトのメタデータとPyO3の依存関係(バージョン)などが記載されています。また、pyproject.tomlにはビルドツールとしてmaturinが使用されることなどがあらかじめ定義されています。

注目すべきはRustスクリプトを記述するsrc/lib.rsファイルです。以下のようなscaffold(足場)が最初から記載されています。

src/lib.rsにデフォルトで記載されているscaffold
 1   use pyo3::prelude::*;
 2   
 3   /// Formats the sum of two numbers as string.
 4   #[pyfunction]
 5   fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
 6       Ok((a + b).to_string())
 7   }
 8   
 9   /// A Python module implemented in Rust.
10   #[pymodule]
11   fn strcounter(_py: Python, m: &PyModule) -> PyResult<()> {
12       m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
13       Ok(())
14   }

Pythonであれば関数はdefキーワードが使用されますが、Rustではfnキーワードが使用されます。scaffoldの主要部分は、Python関数であることを表す#[pyfunction]属性でマークされたsum_as_string()関数(4、5行目)と、Pythonモジュールであることを表す#[pymodule]属性でマークされたstrcounter()関数(10、11行目)です。strcounter()関数が行っていることはsum_as_string()関数をモジュールとして登録することだけです。

sum_as_sting()は文字列の連結を行う関数ですが、この部分を、本記事のサンプルコードcount_chars()関数をRust化して置き換えるのが次のステップです。

関数をRust化して書き換える

先述のとおり、Rustを理解して書き進めていくにはそれなりの学習時間を必要とします。詳細な解説を本記事で行っていくには限界があるため、本記事では最低限必要な部分の説明のみとしています。とはいえ、本サンプルの関数自体は数行のコードで、Pythonのサンプルコードともほぼ同じような構成です。Rustのコードを初めて見るという方もいると思いますが、とりあえず雰囲気で読んでみてください(笑⁠⁠。maturinに触れることがRustを少し勉強してみようと思うきっかけになれば幸いです(筆者もまだまだビギナーです!⁠[2]

Rust化にあたり、lib.rsファイルの変更点は以下の2点です。

  • sum_as_string()関数を削除してcount_chars()関数を作成
  • 27行目のwrap_pyfunction!(sum_as_string, m)wrap_pyfunction!(count_chars, m)に変更

修正後のlib.rsファイルは以下のようになります。

lib.rs ―Rust化されたcount_chars()関数
 1   use pyo3::prelude::*;
 2   
 3   #[pyfunction]
 4   fn count_chars(s: &str) -> (usize, usize, usize) {
 5       // カウント用変数定義
 6       let mut alphabet_count = 0;  // 英字カウント用
 7       let mut digit_count = 0;  // 数値カウント用
 8       let mut other_count = 0;  // その他の文字カウント用
 9   
10       for c in s.chars() {  // 文字列をループで繰り返し
11           if c.is_ascii_alphabetic() {  // アルファベットかどうか
12               alphabet_count += 1;
13           } else if c.is_ascii_digit() {  // 数値かどうか
14               digit_count += 1;
15           } else {
16               other_count += 1;
17           }
18       }
19   
20       (alphabet_count, digit_count, other_count)
21   }
22   
23   
24   /// A Python module implemented in Rust.
25   #[pymodule]
26   fn strcounter(_py: Python, m: &PyModule) -> PyResult<()> {
27       m.add_function(wrap_pyfunction!(count_chars, m)?)?;  // sum_as_stringをcount_charsに変更
28       Ok(())
29   }

以上で、Pythonで書かれていたプログラムを同じ処理を行うRust版のコードに書き換える作業は完了です。

作成したRustのコードをビルドする

作成したstrcounterパッケージをビルドするには、コマンドでmaturin developを実行します。ビルドを実行すると、Rustパッケージがダウンロード、コンパイルされ、仮想環境venvにインストールされます。

maturin developコマンドによるビルドの様子
(strcounter)$ maturin develop
    Updating crates.io index
   〈省略〉
  Downloaded 6 crates (1.6 MB) in 0.80s
🔗 Found pyo3 bindings
   〈省略〉
   Compiling strcounter v0.1.0 (/home/ubuntu/..../strcounter)
    Finished dev [unoptimized + debuginfo] target(s) in 18.15s
📦 Built wheel for CPython 3.11 to /tmp/.tmpyBRxla/strcounter-0.1.0-cp311-cp311-linux_x86_64.whl
🛠 Installed strcounter-0.1.0

作成したスクリプトに問題がある場合には、コンパイルに失敗しエラーが表示されます。コンパイルが問題なく完了すると、上記のようにInstalled....のようなメッセージが表示され終了します。

ちなみに、成功した際のメッセージにBuilt wheel for CPython 3.11 to /tmp/.tmpyBRxla/strcounter-0.1.0-cp311-cp311-linux_x86_64.whlとあるように、Pythonのwheel形式(.whl)のバイナリが作成され、パッケージ化されていることがわかります。

ビルドしたRustパッケージをPythonバインディングとしてインポートする

それでは実際に使用してみましょう。インポートはimport strcounterとするだけです。関数として呼び出すにはstrcounter.count_chars()のようにします。特別な使用方法もなく、Pythonを普段から使用しているやり方と同じなのは親近感が湧きます。

example1.py ―Pythonバインディングとしてインポートして使用するサンプルスクリプト
import strcounter


if __name__ == "__main__":
    text = "Python Monthly Topics: 2023年7月"
    alphabet_count, digit_count, other_count = strcounter.count_chars(text)  # Pythonバインディングを使用
    print(f"アルファベットの数: {alphabet_count}")
    print(f"数字の数: {digit_count}")
    print(f"それ以外の文字数: {other_count}")

実行結果は以下のとおりです。結果を見ると、正しく動作していることが確認できます。

$ python example1.py
アルファベットの数: 19
数字の数: 5
それ以外の文字数: 6

最適化ビルド

バインディングのビルドはmaturin developコマンドを使用して行いました。このコマンドはRustクレートのdevバージョンをビルドするもので、コンパイル時間を短縮するために最適化はスキップされています。

開発したパッケージのパフォーマンスを最大化するには、最適化してビルドを行う必要があります。最適化を行うには、strcounterディレクトリに戻ってmaturin develop --releaseと実行します。--releaseフラグを付けることで、デバック情報や使用されていないデッドコードの除去などが行われ、最適化されたバイナリが作成されます。

ただし、最適化はビルド時間が長くなります。そのため、開発中やデバッグ時には--releaseフラグを使わず、最終的なリリースの際に使用するのが一般的です。フラグを付け忘れてしまうとパフォーマンスが出ません。実際にリリースを行う際には、最適化を忘れないようにしましょう。

--releaseフラグを付けたビルドの様子
(strcounter)$ maturin develop --release
  〈省略〉
   Compiling strcounter v0.1.0 (/home/ubuntu/.../strcounter)
    Finished release [optimized] target(s) in 1m 35s
📦 Built wheel for CPython 3.11 to /tmp/.tmpHVs9Jg/strcounter-0.1.0-cp311-cp311-linux_x86_64.whl
🛠 Installed strcounter-0.1.0

標準出力にFinished release [optimized] target(s) in 1m 35sと表示されているように、ビルドに約1分半かかりました。devバージョンのビルドは20秒弱で終了していたことと比較しても、最適化には時間がかかることがわかります。

パフォーマンス比較

最適化が完了したところで、実際にパフォーマンスがどれほど違うか見てみます。example_base.pyを修正して、元々のPythonのコードとRust化されたPythonバインディングの両方の関数を実行するコードを作成します。計測にはtime.timeitモジュールを使用し、100回実行した平均値を比較します。

また、計算に使用する文字列が少ないとそれほど差が出ないので、長めの文字列を使用してみます。今回は先月(2023年6月)Python Monthly Topicsの記事を使用してみることにしました。

計測用のコードでは、requestsモジュールを使用して上記記事URLのテキストを取得し計算します。事前にrequestsモジュールを以下のコマンドでインストールします。

$ pip install requests
example2.py ―処理時間の計測をtimeitモジュールで行うサンプルスクリプト
import requests
import strcounter
from timeit import timeit


# 文字列を引数として英字、数値、その他の文字列をカウントした結果を返す
def count_chars(text):
    # カウント用変数定義
    alphabet_count = 0  # 英字カウント用
    digit_count = 0  # 数値カウント用
    other_count = 0  # その他の文字カウント用

    for char in text:  # 文字列をループで繰り返し
        if char.isalpha() and char.isascii():  # アルファベットかどうか
            alphabet_count += 1
        elif char.isdigit():  # 数値かどうか
            digit_count += 1
        else:
            other_count += 1

    return alphabet_count, digit_count, other_count


if __name__ == "__main__":
    # 特定のURLからテキストを抽出
    url = "https://gihyo.jp/article/2023/06/monthly-python-2306"
    res = requests.get(url)
    res.encoding = "utf-8"
    text = res.text

    loop = 100  # 繰り返し実行回数
    # 本スクリプト内のPython関数を実行結果を表示
    p_res = timeit(lambda: count_chars(text), number=loop)
    p_time = p_res / loop * 1_000_000  # 1回あたりの平均実行時間をマイクロ秒で計算
    print(f"Python Avg: {p_time:.2f} μs/call")

    # PythonバインディングによるRustの実行結果を表示
    r_res = timeit(lambda: strcounter.count_chars(text), number=loop)
    r_time = r_res / loop * 1_000_000  # 1回あたりの平均実行時間をマイクロ秒で計算
    print(f"Rust Avg: {r_time:.2f} μs/call")

なお、計測では実行結果を表示していませんが、どちらも同じ計算結果になることを確認しています。参考までに出力結果は以下のとおりです。

アルファベットの数: 21188
数字の数: 4911
それ以外の文字数: 20102

計測の実行結果は以下のようになりました。なんと驚きです! Pythonバインディングを経由した方が約40倍も速い結果となりました! たったこれだけの手間で数倍から数十倍の性能向上が期待できるなら、自分が書いたあんなロジックやこんなロジックもRustに任せることができるかもしれません!

$ python example2.py
Python Avg: 7723.11 μs/call
Rust Avg: 193.54 μs/call

「Python 🤝 Rust」は意外と簡単⁠それでも「銀の弾丸など無い」

maturinは想像以上にPythonへの取り回しがよく、しかも圧倒的なパフォーマンスを得られる可能性があることはとても魅力的です。

しかし、いくらRustが速いとはいえPyO3/maturinであらゆるロジックを置き換えられるというわけではありません。安易に使用したらパフォーマンスが得られなかった、ということもあります。

1つ例をあげてみます。以下のようなPythonのreモジュールを使用して、正規表現パターン、文字列、置換する文字列を引数として置換を行う関数を作成しました(サンプルスクリプトの実行結果は、Hello-Python-Rust-with-maturinと表示されるだけです⁠⁠。

regex_sub.py ―reモジュールを使用した置換関数の例
import re


# 引数で指定された正規表現を使用して、テキストを置換する関数
def replace_with_pattern(pattern, text, replacement):
    return re.sub(pattern, replacement, text)


if __name__ == "__main__":
    pattern = r"\d+"  # 正規表現パターン
    text = "Hello01Python23Rust45with678maturin"  # 置換対象文字列
    replacement = "-"  # 置換する文字列

    # 本スクリプト内のPython関数を実行結果を表示
    print(replace_with_pattern(pattern, text, replacement))

これをmaturinで置き換えると、以下のようなlib.rsで実現できます。最初のサンプルと異なるのは、replacerという名前で作成し、Rustで正規表現を使用するためにregexクレートを指定しているところです。

lib.rs ―regexクレートを使用した正規表現のサンプルスクリプト
use pyo3::prelude::*;
use regex::Regex;

// 引数で指定された正規表現を使用して、テキストを置換する関数
#[pyfunction]
fn replace_with_pattern(pattern: &str, text: &str, replacement: &str) -> String {
    let re = Regex::new(pattern).unwrap();
    re.replace_all(text, replacement).to_string()
}

/// A Python module implemented in Rust.
#[pymodule]
fn replacer(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(replace_with_pattern, m)?)?;
    Ok(())
}

以下は参考情報ですが、regexクレートを使用する場合は、Cargo.tomlの[dependencies]属性に以下のように依存関係を追記する必要があります。

[dependencies]
pyo3 = "0.19.0"
regex = "1"  # regexを追加

lib.rsファイルにRustのコードを記載したらmaturin develop --releaseコマンドでビルドを行い、計測用のスクリプトを以下のように作成しました。計測は前回と同じくtime.timeit モジュールで行っています。

regex_sub2.py ―Pythonのreモジュールとの速度比較用スクリプト
import re
import replacer
from timeit import timeit


# 引数で指定された正規表現を使用して、テキストを置換する関数
def replace_with_pattern(pattern, text, replacement):
    return re.sub(pattern, replacement, text)


if __name__ == "__main__":
    pattern = r"\d+"  # 正規表現パターン
    text = "Hello01Python23Rust45with678maturin"  # 置換対象文字列
    replacement = "-"  # 置換する文字列

    loop = 100  # 繰り返し実行回数

    # 本スクリプト内のPython関数を実行
    p_res = timeit(lambda: replace_with_pattern(pattern, text, replacement), number=loop)
    p_time = p_res / loop * 1_000_000  # 1回あたりの平均実行時間をマイクロ秒で計算
    print(f"Python Avg: {p_time:.2f} μs/call")

    # Pythonバインディングによる実行
    r_res = timeit(lambda: replacer.replace_with_pattern(pattern, text, replacement), number=loop)
    r_time = r_res / loop * 1_000_000  # 1回あたりの平均実行時間をマイクロ秒で計算
    print(f"Rust Avg: {r_time:.2f} μs/call")

計測結果は以下のように、圧倒的にPythonの方が速い結果となりました。

$ python regex_sub2.py
Python Avg: 5.76 μs/call
Rust Avg: 822.12 μs/call

これは、Pythonバインディングを使用する際にオーバーヘッドがあるために起きています。つまり、Pythonでもそれほど処理コストがかからない処理にRustを使用しても、Rustのメリットを受けられないということです。他にも巨大なリストをPythonバインディング経由で処理することも試してみましたが、こちらはほぼ同じくらいの処理時間になり、それほどメリットがある結果にはなりませんでした。

このような結果から、maturinを使用する際に考慮するべきこととして、以下のことが言えます。

  • Python-Rust間のオーバーヘッド
    • Pythonで処理負荷が低いものをRust化してもメリットが出ないことがある
    • 比較的大きいオブジェクトのやりとりは、オーバーヘッドを考慮して実装する必要がある
  • CPUバウンドな処理が多い場合にはRustの恩恵を受けられる可能性が高い
  • IOバウンドな処理に採用してもメリットが出ないことがある
    • IOバウンドな処理+その後の処理負荷を考慮して検討する

まとめ

本記事では、maturinを使用してRustによるPythonバインディングの作成方法を紹介しました。また、Rustに書き換える前のPythonコードと処理時間を計測して、高速化が行われていることを確認しました。

Rustはデータ分析など計算コストが高いプログラムを高速に処理することが得意です。maturinを使用すれば、Pythonの使いやすさを維持したまま、Rustを取り入れたパフォーマンス向上を効率的に行うことができるようになります。

もちろん、わざわざRustを使用せずにPythonで高速化を行えるのが一番よいですが、maturinによってPythonとRustがより身近になり、パフォーマンスを向上させる手法が1つ増えたと言えます。

本記事が、Pythonのパフォーマンスで重要な処理をRustで書き換えてみるきっかけになれば幸いです。

おすすめ記事

記事・ニュース一覧