我编写了一个基准测试,该基准计算出第一个10000个素数的总和,并将Rust与JavaScript进行了比较。Nodejs上的JavaScript是Rust,Scala和Java中最快的。即使该程序有意使用功能样式来测试旨在显示Rust零成本抽象的优势的原始性,但Nodejs还是击败了它们。
nodejs是一个动态打字运行时,如何快?
生锈代码
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;
}
可以在GitHub上找到完整的基准。
答案不能简单,因为v8对转换的 lot ,但这是一个主要点:
节点的优化编译器会动态调整其使用的类型(尤其是对于数组元素)。它可以使用一个单词整数时使用(并在接收非拟合值时deoptimize )。
如果我按照您的功能,当节点仅需1.04ms(在加热后)时,生锈的人需要1.28ms来计算sum_prime(500)
。如果我将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 Code快,但比此较慢:
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());
}
我认为您的基准有些缺陷,因为即使在编译时(即prepack,closure),即使在编译时,足够高级的编译器也可以将sum_primes(10000)
优化到496165411
中。也可以在运行时第一次通话后记住结果,这可能是V8所做的(尽管我希望Hotspot会做同样的事情)。
使用一个在编译时间而不是10000
的值,例如命令行参数。