要約
C++ だと 11.44 秒かかるものが Rust だと 18.36 秒もかかって遅い。なぜだ?
議論する内容
Leibniz 級数というものを使って円周率を計算するプログラムを Rust と C++ で実装して速度を比較する。実行時に級数の項の数を引数として渡して計算させる。うまくプログラムが書けていれば、Rust と C++/Clang ではほとんど性能が変わらないはずだが…
Rust でのコード・コンパイル・実行
leibniz.rs
use std::env;
fn powersign(n: i64) -> i64 {
if n % 2 == 0 {
1
} else {
-1
}
}
fn leibniz(n: i64) -> f64 {
let mut s: f64 = 0.0;
for k in 0..=n {
s += powersign(k) as f64/ (2 * k + 1) as f64;
}
4.0 * s
}
fn main() {
let args: Vec<String> = env::args().collect();
let n = &args[1].parse::<i64>().unwrap();
println!("{}", leibniz(*n));
}
コンパイル:
$ rustc -C opt-level=3 -o leibniz-rust leibniz.rs
実行:
$ time ./leibniz-rust 10000000000
3.141592653688346
________________________________________________________
Executed in 19.24 secs fish external
usr time 18.36 secs 0.16 millis 18.36 secs
sys time 0.04 secs 1.74 millis 0.04 secs
C++ での実装・コンパイル・実行
leibniz.cpp
#include <cstdint>
#include <sstream>
#include <iostream>
#include <limits>
template <typename T>
auto is_even(T n) {
return n % T{2} == T{0};
}
template <typename T>
auto powersign(T n) {
return is_even(n) ? T{1} : T{-1};
}
template <typename T>
auto leibniz(T n) {
using F = double;
auto s = F{0};
for (auto k = T{0}; k <= n; k++) {
s += static_cast<F>(powersign(k)) / static_cast<F>(T{2} * k + T{1});
}
return F{4} * s;
}
int main(int, char * argv[]) {
std::stringstream stream;
stream << argv[1];
int64_t n;
stream >> n;
typedef std::numeric_limits<double> lim;
std::cout.precision(lim::max_digits10);
std::cout << leibniz(n) << std::endl;
}
コンパイル:
$ clang++ -O3 -o leibniz-c++ leibniz.cpp
実行:
$ time ./leibniz-c++ 10000000000
3.1415926536883458
________________________________________________________
Executed in 11.50 secs fish external
usr time 11.44 secs 0.11 millis 11.44 secs
sys time 0.03 secs 1.02 millis 0.03 secs
考察
C++ で実装し Clang-15.0.7 でコンパイルしたものだと 11.44 秒かかるものが Rust/rustc-1.67.1 だと 18.36 秒もかかる。遅すぎる。
昔(数年前)、同様のコードで速度差をテストしたときは、差異はほとんどなかったはずなのに、なぜなのだろうか。コンパイラが悪くなってしまったのだろうか。なぜ Rust のコードが遅いのかわかる方がいらっしゃったら教えて下さい。