全人類の夢を叶える究極の言語...か?を夏の自由研究(大人版)でやってみたよ
Stack Overflow Developer Survey でも開発者にLoveされる言語不動の一位、最近機運が高まっているように見えるプログラミング言語Rust。
JVM
のような仕組みは廃してコンパイル後の実行ファイル生成は各OSごとに別々にすることで、Javaなどの往年の既存言語よりも高速なラインをGo言語とともに確保しています。
そしてメモリ解放を自動的に行ってくれるガベージコレクション(GC)の仕組みは多くの言語で採用されていますが、一定期間で自動的に行われるために、その間実行速度は下がってしまうと言われています。Go言語はGCは採用していますが、Rustはこれをも排除。代わりに所有権やライフタイムなどの言語仕様上の仕組みでメモリ安全性を確保するという解法を採りました。
最速レベルを誇っているネイティブC/C++に匹敵する実行速度、さらに所有権周りで間違ってもコーディング段階でコンパイルエラーで教えてくれるので実行前にメモリ安全性を確保。20世紀から続くメモリ管理の悪夢から人類を解放しつつ最速を実現、かつ諸々の新しい仕組みを備えたモダンなプログラミング言語...ということで今こそ全人類、じゃなかった全プログラマーの夢が叶うんじゃないかと注目されています。
夏の自由研究的に夏休み中に実際にRustに入門して触ってみたので、ついでに簡単ですが速度も測ってみました。
- 全人類の夢を叶える究極の言語...か?を夏の自由研究(大人版)でやってみたよ
- 計測のお題
- 計測結果:やっぱりRustは爆速!
- 考察:GoとGo角?(ダジャレ言いたいだけ)
- まとめ:Rust最速物語はほんとだったぴょい
計測のお題
入門の有名定番本として知られる『Rustプログラミング入門』通称「歯車本」の11章「いろいろなRustの発展的Tips」に、他の言語からRust言語をコール、あるいはその逆ができるFFI(Foreign Function Interface)
という仕組みについて解説されています。お互いの言語側で事前に設定が必要なのですが、引数付きの関数レベルでの指定から、実際にRustの処理が呼べます。入門の一環としてこの本は読んでみました。
同書で載っているサンプル関数 add_array(n: u64, x: u64) -> u64
は、「引数nの数だけ要素を持った配列を用意、各要素に+1していく。これを引数x回繰り返して各要素の合計値を最後に返す(戻り値はn*xになる)」というものです。
引数の数値がn: 1000, x: 1000
で戻り値が1,000,000
になる場合でも、同書の環境でRustだと実行に0.3秒弱、Rubyだと6.3秒。既に20倍以上(!)の速度が出ることが記述されています。
このサンプルコードを拝借して、他の言語でも同じ処理の関数を定義して、幾つかのパターンで実行してmacOSのtime
コマンドで測ってみるという方法を採りました。環境はプライベートで使っている古めのMacBook Pro(3.1GHzでメモリ8GB)です。本当は何回も測って平均値を出したほうがより正確なのですが、そこまではやりませんでした。
今回の比較対象のプログラミング言語はRuby、Python、PHP、Node.js上で動くJavaScript、Java、Goです。ワタクシはその昔ポインタがよく理解できずにC/C++に挫折してしまった人間なので、C/C++との比較はやっていません(死)
計測結果:やっぱりRustは爆速!
表にすると以下になりました。
引数nとx | Ruby 2.6.8 |
Python 3.9.13 |
PHP 7.4.3 |
JavaScript (Node.js 16.16.0) |
Java 18.0.2 |
Go 1.18 |
Rust 1.62.1 |
---|---|---|---|---|---|---|---|
1万 | 6.8秒 | 8.6秒 | 4.8秒 | 0.4秒 | 0.2秒 | 0.54秒 | 0.42秒 |
10万 | 11分26秒 | 15分58秒 | 7分56秒 | 11.8秒 | 8.5秒 | 4.7秒 | 4.9秒 |
100万 | 未測定 | 未測定 | 未測定 | 23分16秒 | 14分23秒 | 10分5秒 | 10分13秒 |
200万 | 未測定 | 未測定 | 未測定 | 1時間26分5秒 | 58分31秒 | 45分10秒 | 43分37秒 |
1万個の配列に1万回演算
圧倒的に遅いのがPythonで8秒台、Rubyも6秒台、そして意外と(というと失礼ですがw)PHPが健闘していて4秒台。
JavaScriptだけは0.4秒と、コンパイル型言語に負けない速度を出しています。Node.jsが内部の仕組みで頑張っているのでしょうか。
コンパイル型言語はどれも速く0.5秒以内をキープ。Java、Go、Rustの3言語とももう一度コンパイルすると実行速度は0.2〜0.5秒台の間で揺れがあるので、正直この辺は誤差でしょう。GoもRustも、同じ実行ファイルで同じ関数を再実行すると内部でメモリキャッシュ的な仕組みが効いたりするのか、2回めからは0.1秒を切ってさらに速くなりました。インタープリター型言語はどれも2回め以降の実施も一定の速度でした。
Rustは開発中はコマンド > cargo buiild
でビルドした実行ファイルは開発用なので実は実行速度は遅く、> cargo build --release
したものがリリース用ビルドでここで初めて爆速になるというのは実際に触って初めて知りました。デバッグビルドだとお題の関数実行で6.1秒、リリースビルドで上記の0.4秒と、互いに14倍ぐらい違います。
このデータ1万件の時点でRustはRubyの約16倍、Pythonの20倍、PHPの約11倍の速度を達成。Goとはほぼ互角で、Javaもまだ健闘しているとなります。さあ、スピードの向こう側へ!
10万個の配列に10万回演算
Rubyは11分、Pythonが15分、PHPも8分と一気に遅くなります。JavaScriptだけは11秒とまだ健闘。先頭集団を突っ走るGoとRustは4秒台でほぼ互角、ゴーファー君がカニさんのフェリスを抜いて僅かにリード! そして往年のベテランJava選手は8秒台とついに先頭から脱落。天才はいる、悔しいが...!
ここでRustはRubyの約140倍、Pythonの約170倍、PHPの約98倍、JavaScriptの2.4倍、Javaの1.8倍を達成していることになります。ここでもGoとほぼ互角です。
100万個の配列に100万回演算
結果が返ってこなくなるので(実際には動いてるけど)、インタープリター型言語は測定を断念しました。
最後尾はJavaScriptで23分台。Javaも差が広がり14分台。先頭集団ではGoが譲らない、その速さは、自由か孤独か...!
RustはJavaの1.4倍、JSの2.3倍を達成していることになります。RustはGoに僅かに負けていますが600秒のうちの7秒なので、まあほぼ互角でしょうか。
200万個の配列に200万回演算
フロントエンドの威信をかけて(嘘)脱落を拒絶していたJavaScriptはついに1.5時間と疲労が目立つ!Javaもほぼ1時間!
そして先頭集団ではRustが抜き返した!Goが45分のところRustは43分を達成して2分リード。これが新時代の幕開け、新世代の怪物、伝説の始まりか....!?
考察:GoとGo角?(ダジャレ言いたいだけ)
- 代表的なインタープリター型の動的言語の実行速度は今回の測定ではPHP>Ruby>Pythonでした。Python自体の遅さをカバーするようにC/C++製の機械学習やデータ処理関連のライブラリを呼び出すことで、Python自体は糊のグルー言語という立ち位置でエコシステムを形成している...とよく言われますが、速度的なところもうまくお互いに補完しあっているのですね。
- JavaScript(Node.js)がけっこう頑張っています。
- 1990年代生まれのJavaも途中から遅さが目立ってきます。JVMの仕組みを廃した21世紀のモダンな言語であるGoとRustが速度の面では上位互換になっています。
- そしてガベージコレクションの仕組みも持たず、ゼロコスト抽象化の考えを徹底したRustはGoよりもさらに爆速...と言われていますが、今回の測定では勝っているときも負けている時もありほぼGo角、もとい互角ぐらいで大きい差が出ませんでした。
ガベージコレクションの機能があると一定時間後に自動的にメモリ解放の処理が動いてその間だけガクッと処理速度が落ちてまた戻るらしいので、より長時間、より複雑な処理を継続して行うようなプログラムだと差が出るのかもしれませんね。 - Rust言語の入門書を見るとだいたい、条件によってはスクリプト系言語の10倍〜100倍程度の性能が出る場合もある、という感じでその速さが紹介されていたりします。さすがに100倍は盛りすぎじゃろう...と適当に思っていたんですが今回の測定でも最高170倍まで出たので、本当にそうなのですね。これはすごい。
なおこのブログにお立ち寄り頂くような方ならお分かりかと思いますが、単純な関数ひとつでの比較なのでこのデータは参考程度の話半分に留めておいていただければと思います。
たとえばWebアプリケーションだと実行時間の遅延となる要素はネットワーク上の通信時間だったりデータベースとの接続時間や物理ファイルの読み込み、ブラウザ上での画像ファイルやCSSファイルの読み込み時間などがあるので、ここまで大きな違いは出ないと思います。
大きい物理ファイルやDBから大量データを読み込み、メモリ上で大きなデータ構造に展開して負荷の掛かる演算をする...などの大規模データ処理やバッチ機能などだとRustの圧倒的な速度が力を発揮しそうです。
コード一式はこちらにあります。
ククク...LAPRASを始めとするポートフォリオ系サービスにおかれましてはワイのリポジトリの更新を検知して、Rust使いだと誤認なさるとよいですぞ...(悪)
まとめ:Rust最速物語はほんとだったぴょい
あるのは、ただ一度も先頭を譲らなかった事実...とまでは行かずに今回はGoと互角でしたが、Rustの決まり文句のひとつ "Blazingly fast"
(猛烈に速い) が実感できるお試し測定となりました。
なお日本語で読めるRust言語の商業書籍は既に10冊、言語の入門書としては十分な量なのですが、2022/9/28にインプレスから11冊目の新刊『Rustプログラミング完全ガイド 他言語との比較で違いが分かる!』という翻訳本がまた出ます。
WindowsやLinux、ブラウザのFirefoxを始め、海外のテクノロジーリーダー企業がなにかにRust採用...などのニュースはちょいちょい見かけるようになってきました。日本でもこれからRustの機運がいっそう高まっていくかもしれませんね。