How can node.js be faster than c and java? Benchmark comparing node.js, c, java and python

前端 未结 2 1615
孤城傲影
孤城傲影 2020-12-24 07:32

I made a very simple benchmarking program that calculates all the prime numbers up to 10,000,000 in 4 different languages.

  • (2.97 seconds) - node.js (javascrip
相关标签:
2条回答
  • 2020-12-24 08:20

    When I ran your code in python with a new algorithm:

    real    0m3.583s
    user    0m3.297s
    sys     0m0.094s
    

    Faster than the C benchmark you have above. I think that a simpler language helps you design better algorithms, but that is my opinion. (Also can use multiprocessing to make even faster)

    def allPrimes(N):
        is_prime = [1]*N
        # We know 0 and 1 are composites
        is_prime[0] = 0
        is_prime[1] = 0
    
        i = 2
        # This will loop from 2 to int(sqrt(x))
        while i*i <= N:
            # If we already crossed out this number, then continue
            if is_prime[i] == 0:
                i += 1
                continue
    
            j = 2*i
            while j < N:
                # Cross out this as it is composite
                is_prime[j] = 0
                # j is incremented by i, because we want to cover all multiples of i
                j += i
    
            i += 1
        return is_prime
    
    
    
    
    print("total", sum(allPrimes(10000000)))
    
    0 讨论(0)
  • 2020-12-24 08:23

    I spent a couple of days investigating the performance difference between JS/V8 and C, focusing first of all on the Hydrogen IR generated by the V8 engine. However, after making sure that no extraordinary optimizations are present there, I got back to the analysis of the assembly output and it struck me that the answer was a very simple one, boiling down to the couple of sentences in Jay Conrod's blog post on internals of V8:

    According to the spec, all numbers in JavaScript are 64-bit floating point doubles. We frequently work with integers though, so V8 represents numbers with 31-bit signed integers whenever possible.

    The example at hand allows fitting all computations in 32 bits and node.js takes full advantage of that! The C code utilizes the long type, which on OP's (as well as my) platform happens to be a 64-bit type. Thus, it is a 32-bit arithmetic vs 64-bit arithmetic issue, mostly due to the expensive division/remainder operation.

    If long in the C code is replaced with int, then the binary produced by gcc beats node.js.

    Also, if the loop is made to look for primes over a range that is outside the realm of 32-bit numbers the performance of the node.js version drops significantly.


    Proof

    The used source code is found further in the answer, below the results.

    Counting primes less than 10 million with C and node.js

    $ gcc count_primes.c -std=c99 -O3 -lm -o count_primes_long
    $ sed 's/long/int/g; s/%li/%i/g' count_primes.c > count_primes_int.c
    $ gcc count_primes_int.c -std=c99 -O3 -lm -o count_primes_int
    
    # Count primes <10M using C code with (64-bit) long type
    $ time ./count_primes_long 0 10000000
    The range [0, 10000000) contains 664579 primes
    
    real    0m4.394s
    user    0m4.392s
    sys 0m0.000s
    
    # Count primes <10M using C code with (32-bit) int type
    $ time ./count_primes_int 0 10000000
    The range [0, 10000000) contains 664579 primes
    
    real    0m1.386s
    user    0m1.384s
    sys 0m0.000s
    
    # Count primes <10M using node.js/V8 which transparently does the
    # job utilizing 32-bit types
    $ time nodejs ./count_primes.js 0 10000000
    The range [ 0 , 10000000 ) contains 664579 primes
    
    real    0m1.828s
    user    0m1.820s
    sys 0m0.004s
    

    Performance figures in the vicinity of the limit of signed 32-bit integers

    Counting the primes in the range of length 100,000 starting at the number contained in the first column:

                  | node.js | C (long) 
    -----------------------------------
    2,000,000,000 | 0.293s  | 0.639s    # fully within the 32-bit range
    -----------------------------------
    2,147,383,647 | 0.296s  | 0.655s    # fully within the 32-bit range
    -----------------------------------
    2,147,453,647 | 2.498s  | 0.646s    # 50% within the 32-bit range
    -----------------------------------
    2,147,483,647 | 4.717s  | 0.652s    # fully outside the 32-bit range
    -----------------------------------
    3,000,000,000 | 5.449s  | 0.755s    # fully outside the 32-bit range
    -----------------------------------
    

    count_primes.js

    "use strict";
    
    var isPrime = function(n){
        if (n < 2) {return false};
        if (n === 2) {return true};
        if (n === 3) {return true};
        if (n % 2 === 0) {return false};
        if (n % 3 === 0) {return false};
        var sqrtOfN = Math.sqrt(n);
        for (var i = 5; i <= sqrtOfN; i += 6){
            if (n % i === 0) {return false}
            if (n % (i + 2) === 0) {return false}
        }
        return true;
    };
    
    var countPrime = function(S, E){
        var count = 0;
        for (let i = S; i < E;i++){
            if ( isPrime(i) ) { ++count; }
        }
        return count;
    };
    
    if( process.argv.length != 4) {
        console.log('Usage: nodejs count_prime.js <range_start> <range_length>');
        process.exit();
    }
    
    var S = parseInt(process.argv[2]);
    var N = parseInt(process.argv[3]);
    var E = S+N;
    var P = countPrime(S, E);
    console.log('The range [', S, ',', E, ') contains', P, 'primes');
    

    count_primes.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    #define true 1
    #define false 0
    
    int isPrime (register long n){
        if (n < 2)      return false;
        if (n == 2)     return true;
        if (n == 3)     return true;
        if (n % 2 == 0) return false;
        if (n % 3 == 0) return false;
        double sqrtOfN = sqrt(n);
        for (long i = 5; i <= sqrtOfN; i += 6){
            if (n % i == 0) return false;
            if (n % (i + 2) == 0) return false;
        }
        return true;
    };
    
    int main(int argc, const char * argv[]) {
        if ( argc != 3 ) {
            fprintf(stderr, "Usage: count_primes <range_start> <range_length>\n");
            exit(1);
        }
        const long S = atol(argv[1]);
        const long N = atol(argv[2]);
        register long count = 0;
        for (register long i = S; i < S + N; i++){
            if ( isPrime(i) ) ++count;
        }
        printf("The range [%li, %li) contains %li primes\n", S, S+N, count);
    }
    
    0 讨论(0)
提交回复
热议问题