为什么NodeJS在计算素数之和时比Rust更快?

问题描述 投票:6回答:2

我写了一个基准,计算前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

javascript node.js rust benchmarking v8
2个回答
15
投票

答案不能简单,因为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());
}

-1
投票

我认为你的基准有点缺陷,因为一个足够先进的编译器可以将sum_primes(10000)优化到496165411,即使在编译时(即PrepackClosure)也是如此。也可以在运行时第一次调用后记忆结果,这可能是V8的作用(虽然我希望HotSpot也这样做)。

使用编译时未知的值而不是10000,例如命令行参数。

© www.soinside.com 2019 - 2024. All rights reserved.