Почему NodeJS быстрее, чем Rust, вычисляет сумму простых чисел? - PullRequest
0 голосов
/ 22 февраля 2019

Я написал тест, который вычисляет сумму первых 10000 простых чисел и сравнил Rust с JavaScript.JavaScript на NodeJS является самым быстрым среди 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 .

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Ответ не может быть простым, потому что V8 выполняет лот преобразований, но вот важный момент:

Оптимизирующий компилятор узла динамически адаптирует используемые им типы (особенно для элементов массива).Он может использовать одно слово целые числа, когда они подходят (и деоптимизирует функцию, когда она получает несоответствующее значение).

Если я возьму ваши функции такими, как они есть, то Rust получит 1,28мс для вычисления sum_prime(500), когда Node занимает всего 1,04 мс (после некоторого прогрева).Если я изменим u64 на u32 в коде Rust, то это займет всего 608 мкс.


Код 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());
}
0 голосов
/ 22 февраля 2019

Я думаю, что ваш тест несколько ошибочен, так как достаточно продвинутый компилятор может просто оптимизировать sum_primes(10000) в 496165411, даже во время компиляции (то есть Prepack , Closure ),Также возможно запомнить результат после первого вызова во время выполнения, и, вероятно, это то, что делает V8 (хотя я ожидал бы, что HotSpot сделает то же самое).

Используйте значение, которое не известно ввремя компиляции вместо 10000, например аргумент командной строки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...