gpt4 book ai didi

java - node.js 如何比 c 和 java 更快?基准比较 node.js、c、java 和 python

转载 作者:IT老高 更新时间:2023-10-28 23:05:17 32 4
gpt4 key购买 nike

我制作了一个非常简单的基准测试程序,可以计算 4 种不同语言中最多 10,000,000 的所有质数。

  • (2.97 秒)- node.js(javascript)(4.4.5)
  • (6.96 秒) - c (c99)
  • (6.91 秒) - java (1.7)
  • (45.5 秒)- python (2.7)

  • 以上是平均每次运行 3 次,用户时间

    Node.js 运行速度最快。这让我感到困惑,原因有两个:
  • javascript 总是对变量使用 double 浮点数,而 c 和 java 在这种情况下使用(长)整数。整数数学应该更快。
  • javascript 通常被称为解释型语言,而实际上它是一种即时编译语言。但即便如此,JIT 编译器怎么能比完全编译的语言更快呢?
    python 代码运行最慢,这并不奇怪,但是为什么 node.js 代码的运行速度与 python 不同?

  • 我用 -O2 优化编译了 c 代码,但我尝试了所有级别的优化,并没有产生明显的差异。

    countPrime.js
    "use strict";

    var isPrime = function(n){
    //if (n !== parseInt(n,10)) {return false};
    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};
    if (n % 1) {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(){
    var count = 0;
    for (let i = 1; i < 10000000;i++){
    if (isPrime(i)){
    count++;
    }
    }
    console.log('total',count);
    };

    countPrime();

    node.js 结果
    $ time node primeCalc.js
    total 664579

    real 0m2.965s
    user 0m2.928s
    sys 0m0.016s

    $ node --version
    v4.4.5

    素数计算器
    #include <stdio.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;
    if (n % 1) 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[]) {
    register long count = 0;
    for (register long i = 0; i < 10000000; i++){
    if (isPrime(i)){
    count++;
    }
    }

    printf("total %li\n",count);
    return 0;
    }

    c 结果
    $ gcc primeCalc.c -lm -g -O2 -std=c99 -Wall
    $ time ./a.out
    total 664579
    real 0m6.718s
    user 0m6.668s
    sys 0m0.008s

    PrimeCalc.java

    公共(public)类 PrimeCalc {
      public static void main(String[] args) {
    long count = 0;
    for (long i = 0; i < 10000000; i++){
    if (isPrime(i)){
    count++;
    }
    }
    System.out.println("total "+count);
    }


    public static boolean isPrime(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;
    if (n % 1 > 0) return false;
    double sqrtOfN = Math.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;
    };

    }

    结果
     $ javac PrimeCalc.java 
    $ time java PrimeCalc
    total 664579
    real 0m7.197s
    user 0m7.036s
    sys 0m0.040s
    $ java -version
    java version "1.7.0_111"
    OpenJDK Runtime Environment (IcedTea 2.6.7) (7u111-2.6.7-0ubuntu0.14.04.3)
    OpenJDK 64-Bit Server VM (build 24.111-b01, mixed mode)

    素数计算器
    import math

    def isPrime (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
    if n % 1 >0 : return False
    sqrtOfN = int(math.sqrt(n)) + 1
    for i in xrange (5, sqrtOfN, 6):
    if n % i == 0 : return False;
    if n % (i + 2) == 0 : return False;
    return True

    count = 0;
    for i in xrange(10000000) :
    if isPrime(i) :
    count+=1

    print "total ",count

    python 结果
    time python primeCalc.py
    total 664579
    real 0m46.588s
    user 0m45.732s
    sys 0m0.156s
    $ python --version
    Python 2.7.6

    linux
    $ uname -a
    Linux hoarfrost-node_6-3667558 4.2.0-c9 #1 SMP Wed Sep 30 16:14:37 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

    额外的 c 运行时间(附录)
  • (7.81 s) 没有优化,gcc primeCalc.c -lm -std=c99 -Wall
  • (8.13 秒)优化 0,gcc primeCalc.c -lm -O0 -std=c99 -Wall
  • (7.30秒)优化1,gcc primeCalc.c -lm -O1 -std=c99 -Wall
  • (6.66秒)优化2,gcc primeCalc.c -lm -O2 -std=c99 -Wall
  • 每个优化级别用户时间平均 3 次新运行 *

  • 我在这里阅读了帖子:
    Why is this NodeJS 2x faster than native C?
    此代码使用的示例实际上并没有做任何重要的事情。就好像编译器可以在编译时计算出结果,甚至不需要执行循环 100000000 次就可以得出答案。
    如果在计算中添加另一个除法步骤,则优化的重要性要小得多。
    for (long i = 0; i < 100000000; i++) {
    d += i >> 1;
    d = d / (i +1); // <-- New Term
    }
  • (1.88 秒)未优化
  • (1.53 秒)优化 (-O2)


  • 2017 年 3 月 15 日更新
    在阅读@leon 的答案后,我进行了一些验证测试。

    测试 1 - 32 位 Beaglebone Black,664,579 个质数,最多 10,000,000

    未经编辑的 calcPrime.js 和 calcPrime.c 在具有 32 位处理器的 Beaglebone black 上运行。
  • C 代码 = 62 秒(gcc,长数据类型)
  • JS 代码 = 102 秒( Node v4)

  • 测试 2 - 64 位 Macbook Pro,664,579 个质数高达 10,000,000

    用 uint32_t 替换 calcPrime.c 代码中的长数据类型,并在我的 MacBook pro 上运行,它有 64 位处理器。
  • C 代码 = 5.73 秒(clang,长数据类型)
  • C 代码 = 2.43 秒(clang,uint_32_t 数据类型)
  • JS 代码 = 2.12 秒( Node v4)

  • 测试 3 - 64 位 Macbook Pro,91,836 个素数(i=1;i < 8,000,000,000;i+=10000)

    在 C 代码中使用 unsigned long 数据类型,强制 javascript 使用一些 64 位。
    - C 代码 = 20.4 秒(clang,长数据类型)
    - JS 代码 = 17.8 秒( Node v4)

    测试 4 - 64 位 Macbook Pro,86,277 个质数(i = 8,000,00,001;i < 16,000,000,000;i+=10000)

    在 C 代码中使用 unsigned long 数据类型,强制 javascript 使用所有 64 位。
    - C 代码 = 35.8 秒(clang,长数据类型)
    - JS 代码 = 34.1 秒( Node v4)

    测试 5 - Cloud9 64 位 Linux,(i = 0;i < 10000000;i++)
    language    datatype    time    % to C
    javascript auto 3.22 31%
    C long 7.95 224%
    C int 2.46 0%
    Java long 8.08 229%
    Java int 2.15 -12%
    Python auto 48.43 1872%
    Pypy auto 9.51 287%

    测试 6 - Cloud9 64 位 Linux,(i = 8000000001;i < 16000000000;i+=10000)
    javascript  auto       52.38      12%
    C long 46.80 0%
    Java long 49.70 6%
    Python auto 268.47 474%
    Pypy auto 56.55 21%

    (所有结果都是用户 3 次运行的平均秒数,运行之间的时间变化 < 10%)

    喜忧参半

    在整数范围内将 C 和 Java 数据类型更改为整数可显着加快执行速度。在 BBB 和 Cloud9 计算机上,切换到整数使 C 比 node.js 更快。但是在我的 Mac 上,node.js 程序仍然运行得更快。也许是因为 Mac 使用 clang 而 BBB 和 Cloud 9 计算机使用 gcc。有谁知道 gcc 编译的程序是否比 gcc 更快?

    当使用所有 64 位整数时,C 在 BBB 和 Cloud9 PC 上比 node.js 快一点,但在我的 MAC 上慢一点。

    您还可以看到,在这些测试中,pypy 比标准 python 快四倍。

    node.js 甚至与 C 兼容这一事实让我感到惊讶。

    最佳答案

    我花了几天时间研究 JS/V8 和 C 之间的性能差异,首先关注 V8 引擎生成的 Hydrogen IR。然而,在确定那里没有特别的优化之后,我回到了对汇编输出的分析,让我吃惊的是答案非常简单,归结为 Jay Conrod's blog post 中的几句话。关于 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.



    手头的示例允许以 32 位拟合所有计算,并且 node.js 充分利用了这一点! C 代码使用 long类型,在 OP(以及我的)平台上恰好是 64 位类型。因此,这是一个 32 位算术与 64 位算术问题,主要是由于昂贵的除法/余数运算。

    long在 C 代码中替换为 int ,然后由 gcc 生成的二进制文件击败了 node.js。

    此外,如果循环在 32 位数字范围之外的范围内查找素数,则 node.js 版本的性能会显着下降。

    证明

    使用的源代码在结果下方的答案中进一步找到。

    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



    从第一列中包含的数字开始计算长度为 100,000 范围内的素数:
                  | 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);
    }

    关于java - node.js 如何比 c 和 java 更快?基准比较 node.js、c、java 和 python,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39360403/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com