我测试了“并行估算圆周率”的 Rust 和 Java 代码:
use rand::{thread_rng, Rng};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
const N: i32 = i32::MAX;
fn main() {
let n: i32 = (0..N).into_par_iter().map(|_| {
let mut rng = thread_rng();
let x = rng.gen_range(0.0..1.0) - 0.5f64;
let y = rng.gen_range(0.0..1.0) - 0.5f64;
if x.powf(2.0) + y.powf(2.0) < 0.25 { 1 } else { 0 }
}).sum();
let pi = n as f64 / N as f64 * 4.0;
println!("π ≈ {pi}");
}
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;
public class Main {
static final int N = Integer.MAX_VALUE;
public static void main(String[] args) {
int count = IntStream.range(0, N).parallel().map(i -> {
double x = ThreadLocalRandom.current().nextDouble(0.0, 1.0) - 0.5;
double y = ThreadLocalRandom.current().nextDouble(0.0, 1.0) - 0.5;
return (x * x + y * y < 0.25) ? 1 : 0;
}).sum();
double pi = (double) count / N * 4.0;
System.out.println("π ≈ " + pi);
}
}
Rust版本在我的电脑上执行耗时7秒,而Java耗时仅3秒。 有没有大佬可以解释这是为什么呀?
1
共 9 条评论, 1 页
评论区
写评论Java 版本在我的电脑上耗时 1.7 秒,Rust 版本耗时 3 秒,但是下面这样写的话则只需要 0.58 秒:
其中关键点有两个,一个是因为不需要加密所以换用更快的
SmallRng
(thread_rng()
默认使用加密算法),另一个是改用map_init
来避免大量的随机数生成器初始化所带来的开销。希望每个怀疑性能问题的都起码学一下性能刨析软件如何使用。
例如你这个问题,在Linux下cargo build --release编译后运行perf record -F 99 -a -- ./target/release/<你的程序名>,然后执行perf report,你就可以看到最耗时的是rand::rng::Rng::gen_range以及rand_chacha::guts::refill_wide::impl_avx2,进而猜测出问题出在随机数生成的库上。
和迭代器没关系,就是rng的性能差异;
多线程的程序不好跨机器比较性能,看其他人的回复有不少2-3s的运行时间,我的16c32t机器上运行时间只有0.8s,把原程序改成单线程版本(数据量缩小1/8), cpu用5700u;
运行时间:
运行时间(openjdk 22 2024-03-19):
rust版本改用SmallRng(feature small_rng):
运行时间
看文档
rand
的thread_rng
是 cryptographically secure,但是java
的ThreadLocalRandom
不是,所以rust
的慢很多。换成
fastrand
后,就快了好多:有可能是生成随机数慢:
刚刚看到大佬在知乎上评仓颉,不知道仓颉的版本要多长时间 🤣
.\target\release\siu.exe π ≈ 3.141593321758133 2354ms
2.3秒的路过
没开优化?我rust跑2.8秒