初めに #
プログラミング初心者の方は、「PythonとRustどちらが優れているのだろう?」と感じたことはないでしょうか。
Pythonはシンプルな文法で初心者にとっても扱いやすく AI や データ分析 、Webアプリ開発 など幅広い用途で人気のある言語です。
しかし実行速度に関しては他の言語に比べやや劣ると言われています。
一方、Rustは近年注目されている新しい言語で、C++並みの高速なパフォーマンスと高いメモリ安全性を兼ね備えています。
そこで本記事では、この2つのプログラミング言語の実行速度を簡単なベンチマークで比較し、プログラミング言語比較の観点からどちらが速いかを検証してみます。
検証環境とベンチマーク方法 #
今回、比較に使用する環境は以下の通りです。
また、処理時間の計測にはLinuxの time コマンドを利用します。
| 項目 | 内容 |
|---|---|
| OS | AlmaLinux(Linuxディストリビューション) |
| Python | 3系 |
| Rust | 最新のstable版 |
| 実行環境 | 同一物理スペックの環境で比較 |
| ベンチマーク条件 | 同一処理内容で実行速度を測定 |
処理内容 #
今回のベンチマークでは、「1 から 1億 までの整数を順番に加算し、その合計値を求める処理」 累積和を用います。
例えば、1 から 5 まで の累積和を求める場合の計算は次のような式となります。
1 + 2 + 3 + 4 + 5 =
この処理は非常に単純ではありますが、繰り返しの回数が多くなるにつれて CPU 負荷が高まるため、プログラミング言語の実行性能を測る指標として適しています。
なお、ベンチマークを実行する際は、ご利用の環境(CPUスペックやメモリ容量、制限など)に応じて条件を調整することをお勧めします。
無理に大きな範囲を指定すると、処理が長時間かかる場合やシステムに負荷がかかる可能性があるため注意してください。
Pythonの実装と実行結果 #
Pythonで累積和を計算するコードは以下となっております。
コードは非常にシンプルで、1から1億までの整数をループで足し込むだけとなります。
total = 0
for i in range(1, 100000001):
total += i
print(total)
今回は、この累積和計算の処理を performance.py として保存し、ベンチマーク検証を行います。
それでは、このスクリプトを実行し、time コマンドで実行時間を測定してみます。
$ time python3 performance.py
5000000050000000
real 0m11.393s
user 0m11.014s
sys 0m0.049s
出力結果から、累積和の計算結果は 5000000050000000 となりました。
real は実際に経過した時間、user は CPU がユーザー処理に費やした時間、sys はカーネル処理にかかった時間を示しています。
Python で 1 から 1億までの加算を行った場合、約 11 秒ほどかかる結果となりました。
しかし1回の測定だけでは処理時間にばらつきが出る可能性があります。
そのため、この処理を 10 回実行し、その平均値を取得してベンチマーク結果の信頼性を高めます。
以下のループを用いて、同じ処理を 10 回連続で計測しました。
for i in {1..10}; do
time python3 performance.py
done
複数回の計測結果を比較したところ、いずれも概ね 11 秒前後で推移しておりました。
このことから、該当処理を Python で実行した場合の処理時間は 約 11 秒程度であると判断できます。
| No. | real | user | sys |
|---|---|---|---|
| 1 | 0m11.393s | 0m11.014s | 0m0.049s |
| 2 | 0m11.476s | 0m11.240s | 0m0.013s |
| 3 | 0m11.294s | 0m11.005s | 0m0.025s |
| 4 | 0m11.103s | 0m10.994s | 0m0.013s |
| 5 | 0m11.283s | 0m11.127s | 0m0.007s |
| 6 | 0m11.632s | 0m11.012s | 0m0.010s |
| 7 | 0m11.387s | 0m11.027s | 0m0.061s |
| 8 | 0m11.257s | 0m11.008s | 0m0.033s |
| 9 | 0m11.603s | 0m11.018s | 0m0.020s |
| 10 | 0m11.103s | 0m11.011s | 0m0.013s |
| 平均 | 0m11.353s | 0m11.046s | 0m0.024s |
Rustの実装と実行結果 #
次に同じ処理をRustで実装し、実行時間を測定します。
Rustのコードも基本的には同じアルゴリズムで累積和を計算します。
fn main() {
let mut total: u64 = 0;
for i in 1..=100_000_000 {
total += i;
}
println!("{}", total);
}
上記がRustで書いた累積和計算プログラムとなります。
main 関数内で可変変数 total を u64型として宣言し0を指定し初期化しています。
その後はPythonと同様にループを利用し total 変数に加算しています。
最後に println! マクロを利用し結果を出力する簡単なプログラムとなっております。
プログラムの作成が完了したらRustコードをコンパイルします。
本番相当の最適化を有効化するため、--release フラグを付与してビルドを実行します。
cargo build --release
ビルドが完了したらPythonと同様に time コマンドを利用し実行します。
time performance_rs
以下は実行結果となります。
$ time ./performance_rs
5000000050000000
real 0m0.056s
user 0m0.056s
sys 0m0.000s
Rustでも正しく 5000000050000000 という結果が得られています。
集計データの信頼性を高めるためRustでも同様に測定を10回実行し平均値を取得します。
for i in {1..10}; do
time ./performance_rs
done
複数回の計測結果を比較したところ、いずれも 0.056 秒前後を安定して推移しており非常に速い結果となりました。
| No. | real | user | sys |
|---|---|---|---|
| 1 | 0m0.056s | 0m0.056s | 0m0.000s |
| 2 | 0m0.056s | 0m0.056s | 0m0.000s |
| 3 | 0m0.056s | 0m0.056s | 0m0.000s |
| 4 | 0m0.057s | 0m0.056s | 0m0.000s |
| 5 | 0m0.056s | 0m0.056s | 0m0.000s |
| 6 | 0m0.056s | 0m0.056s | 0m0.000s |
| 7 | 0m0.056s | 0m0.052s | 0m0.004s |
| 8 | 0m0.056s | 0m0.056s | 0m0.000s |
| 9 | 0m0.057s | 0m0.052s | 0m0.004s |
| 10 | 0m0.056s | 0m0.052s | 0m0.004s |
| 平均 | 0m0.0562s | 0m0.0548s | 0m0.0012s |
ベンチマークの比較 #
以下が Python と Rust の平均実行時間の比較結果となります。
| 言語 | real | user | sys |
|---|---|---|---|
| Python | 0m11.353s | 0m11.046s | 0m0.024s |
| Rust | 0m0.0562s | 0m0.0548s | 0m0.0012s |
この表を見ると、Rust の処理は わずか 0.056 秒ほどで終わっているのに対し、Python は 11 秒以上かかっていることが確認できます。
この結果から、Rust の方が約200倍も速く処理を完了していることが分かります。
このように差が出る理由として、Rust は「コンパイル型言語」であることが挙げられます。
Rust のコードはあらかじめパソコンが理解できる「機械語」に変換され、CPU が直接高速に処理できる状態で実行されるため、無駄がありません。
一方で、Python は「インタプリタ型言語」です。
プログラムをその場で1行ずつ読みながら実行していくため、低レイヤー言語と比べると処理に時間がかかります。
Rustは Linuxカーネルの第二言語としても正式に採用されるなど、その信頼性と将来性が高く評価されています。
どちらも素晴らしい言語ですが今後、高速な処理が求められる開発やインフラ寄りの分野を目指すのであれば、 Rust を学んでおくのも選択肢の1つとなるかと思います。