我写了一个基准,计算前10000个素数的总和,并将Rust与JavaScript进行比较。 NodeJS上的JavaScript是Rust,Scala和Java中最快的。尽管程序故意使用功能样式来测试素数,旨在展示Rust的零成本抽象的优势,但NodeJS击败了它们。
动态类型运行时NodeJS如何如此之快?
Rust代码
fn sum_primes(n: usize) -> u64 {
let mut primes = Vec::new();
let mut current: u64 = 2;
let mut sum: u64 = 0;
while primes.len() < n {
if primes.iter().all(|p| current % p != 0) {
sum += current;
primes.push(current);
}
current += 1;
}
sum
}
JavaScript代码
function sumPrimes(n) {
let primes = [];
let current = 2;
let sum = 0;
while (primes.length < n) {
if (primes.every(p => current % p != 0)) {
sum += current;
primes.push(current);
}
++current;
}
return sum;
}
完整的基准可以找到on GitHub。
答案不能简单,因为V8进行了很多转换,但这里有一个重点:
Node的优化编译器动态地调整它使用的类型(特别是对于数组元素)。当它们适合时,它可以使用一个单词整数(当它接收到非拟合值时,可以去除优化函数)。
如果我按原样使用你的函数,当Node只需要1.04ms(经过一些变暖)之后,Rust会花费1.28ms来计算sum_prime(500)
。如果我在Rust代码中将u64
更改为u32
,那么它只需要608μs。
我使用的JavaScript代码:
function sum_primes(n) {
var primes = [];
var current = 2;
var sum = 0;
while (primes.length < n) {
if (primes.every(function (p) { return current % p != 0; })) {
sum += current;
primes.push(current);
}
++current;
}
return sum;
}
console.log(sum_primes(200));
// some warming
for (let i=0; i<100; i++) sum_primes(100);
console.time("primes");
console.log(sum_primes(500));
console.timeEnd("primes");
这个JavaScript代码比Rust代码快,但比这个慢:
use std::time::Instant;
fn sum_primes(n: usize) -> u32 {
let mut primes = Vec::new();
let mut current: u32 = 2;
let mut sum: u32 = 0;
while primes.len() < n {
if primes.iter().all(|p| current % p != 0) {
sum += current;
primes.push(current);
}
current += 1;
}
sum
}
fn main() {
println!("{}", sum_primes(200));
let s = Instant::now();
println!("{}", sum_primes(500));
println!("duration: {:?}", s.elapsed());
}