怎么可能节点.js比c和java快吗?基准比较节点.js、c、java和python

2024-05-15 03:32:26 发布

您现在位置:Python中文网/ 问答频道 /正文

我做了一个非常简单的基准测试程序,用4种不同的语言计算出10000000以内的所有质数。在

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

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

在节点.js跑得最快。这让我感到困惑有两个原因:

  1. javascript总是对变量使用双精度浮点,而在这种情况下,c和java使用(长)整数。用整数计算应该更快。在
  2. javascript通常被称为解释性的,实际上它是一种即时编译语言。但即便如此,JIT编译器如何比完全编译的语言更快呢? python代码运行速度最慢这一点也不奇怪,但为什么节点.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();

在节点.js结果

^{pr2}$

primeCalc.c.公司

#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版在

公共类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;
  };

}

java结果

 $ 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)

在primeCalc.py在

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 s)优化0,gcc primeCalc.c-lm-O0-std=c99-墙
  • (7.30秒)优化1,gcc primeCalc.c-lm-O1-std=c99-墙
  • (6.66 s)优化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)

更新03/15/2017 在阅读了@leon的答案后,我做了一些验证测试。在

测试1-32位Beaglebone Black,664579素数最高10000000

未编辑加州时间.js以及运行在Beaglebone black上的CalcTime.c,它有一个32位处理器。在

  • C代码=62秒(gcc,长数据类型)
  • 代码(v4 JS节点=102秒)

测试2-64位Macbook Pro,664579 prime最高10000000

用uint32替换calcPrime.c代码中的长数据类型,并在我的MacBookPro上运行,它有一个64位处理器。在

  • C代码=5.73秒(叮当声,长数据类型)
  • C代码=2.43秒(clang,uint_32_t数据类型)
  • JS代码=2.12秒(节点v4)

测试3-64位Macbook Pro,91836个素数(i=1;i<;800000000;i+=10000)

在C代码中使用无符号长数据类型,强制javascript使用一些64位。 -C代码=20.4秒(叮当声,长数据类型) -JS代码=17.8秒(节点v4)

测试4位64位Macbook Pro,86277个素数(i=8000,00001;i<;1600000000;i+=10000)

在C代码中使用无符号长数据类型,强制javascript使用所有64位。 -C代码=35.8秒(叮当声,长数据类型) -JS代码=34.1秒(节点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=800000001;i<;1600000000;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%

(所有结果均为平均值三次运行的用户秒数,运行之间的时间变化<;10%)

混合结果

在整数范围内将C和Java数据类型更改为integer可以显著加快执行速度。在BBB和Cloud9上,转换到ints的C比节点.js. 但在我的Mac上节点.js程序仍然运行得更快。也许是因为Mac使用的是clang,BBB和cloud9计算机使用的是gcc。有人知道gcc编译的程序比gcc快吗?在

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

在这些测试中,您还可以看到pypy的速度大约是标准python的四倍。在

事实上节点.js甚至与C兼容也让我吃惊。在


Tags: 代码falsetrueforreturnif节点count
1条回答
网友
1楼 · 发布于 2024-05-15 03:32:26

我花了几天时间研究JS/V8和C之间的性能差异,首先关注V8引擎产生的氢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位和节点.js充分利用这一点!C代码使用long类型,在OP(以及我的)平台上,它恰好是一个64位类型。因此,这是一个32位算术vs 64位算术问题,主要是由于昂贵的除法/余数运算。在

如果C代码中的longint替换,那么gcc生成的二进制文件将跳动节点.js. 在

另外,如果循环是为了在32位数字范围之外的范围内查找素数,则节点.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

从第一列中包含的数字开始计算长度为100000的素数:

^{pr2}$

计数_素描.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');

计算素数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);
}

相关问题 更多 >

    热门问题