programing

어떻게 node.js가 c와 java보다 빠를 수 있습니까?node.js, c, java, python 비교 벤치마크

muds 2023. 9. 28. 08:49
반응형

어떻게 node.js가 c와 java보다 빠를 수 있습니까?node.js, c, java, python 비교 벤치마크

저는 4개의 다른 언어로 10,000,000까지의 모든 소수를 계산하는 아주 간단한 벤치마킹 프로그램을 만들었습니다.

  • (2.97초) - node.js (jav 스크립트) (4.4.5)
  • (6.96초) - c(c99)
  • (6.91초) - 자바(1.7)
  • (45.5초) - 파이썬 (2.7)

위는 각각 평균 3회 실행, 사용자 시간

Node.js가 가장 빠르게 실행됩니다.이것은 두 가지 이유로 저에게 혼란스럽습니다.

  1. javascript는 변수에 대해 항상 이중 정밀도 플로트를 사용하는 반면 c와 java는 이 경우 (긴) 정수를 사용합니다.정수가 있는 수학은 더 빨라야 합니다.
  2. just in time 컴파일된 언어일 때 just in time에서 javascript는 종종 해석된 것으로 언급됩니다.하지만 그렇다고 해도 JIT 컴파일러가 어떻게 완전히 컴파일된 언어보다 빠를 수 있겠습니까?파이썬 코드가 가장 느리게 실행되는 것은 놀랄 일도 아니지만, 왜 node.js 코드가 파이썬과 비슷한 속도로 실행되지 않는 것일까요?

-O2 최적화로 c 코드를 컴파일했지만 모든 최적화 수준으로 시도해 보았는데 눈에 띄는 차이가 없었습니다.

Prime.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

프라임칼크자바

퍼블릭 클래스 프라임칼크 {

  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)

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

파이썬 결과

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

리눅스

$ 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

추가 크럼 시간(부록)

  • (7.81초) 최적화 없음, gcc primeCalc.c - lm - std=c99 - 벽
  • (8.13초) 최적화 0, gcc primeCalc.c - lm - O0 - std=c99 - 벽
  • (7.30초) 최적화 1, gcc primeCalc.c - lm - O1 - std=c99 - 벽
  • (6.66초) 최적화 2, gcc primeCalc.c - lm - O2 - std=c99 - 벽

    • 각 최적화 레벨 사용자 시간마다 평균 3회 새로운 실행 *

여기 게시물을 읽었습니다.이 NodeJS가 네이티브 C보다 2배 빠른 이유는 무엇입니까?이 코드는 실제로 중요한 일을 하지 않는 예제를 사용합니다.마치 컴파일러가 컴파일 시간에 결과를 파악할 수 있고 답을 도출하기 위해 루프를 1000000번 실행할 필요도 없는 것 같습니다.계산에 다른 분할 단계를 추가하면 최적화는 훨씬 덜 중요합니다.

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비트 비글본 블랙, 664,579개의 소수점, 최대 10,000,000개

32비트 프로세서를 가진 비글본 블랙에서 실행되는 편집되지 않은 calcalPrime.js 및 calcalPrime.c.

  • C 코드 = 62초 (gcc, 긴 데이터 타입)
  • JS 코드 = 102초 (노드 v4)

테스트 2 - 64 비트 맥북 프로, 664,579 primes ~ 10,000,000

calcPrime.c 코드의 긴 데이터 유형을 uint32_t로 교체하고 64비트 프로세서가 있는 맥북 프로에서 실행합니다.

  • C 코드 = 5.73초 (clang, long data type)
  • C 코드 = 2.43초 (clang, uint_32_t data type
  • JS 코드 = 2.12초 (노드 v4)

테스트 3 - 64비트 맥북 프로, 91,836 primes (i=1; i < 8,000,000,000; i+=10000)

C 코드에서 부호 없는 긴 데이터 형식을 사용하고, 자바스크립트가 64비트를 사용하도록 강제합니다. - C 코드 = 20.4초 (clang, long data 형식) - JS 코드 = 17.8초 (node v4)

테스트 4 - 64비트 맥북 프로, 86,277개 소수 (i = 8,000,00,001; i < 16,000,000; i+=10000)

C 코드에서 부호 없는 긴 데이터 형식을 사용하고, 자바스크립트가 64비트를 모두 사용하도록 강제합니다. - C 코드 = 35.8초 (clang, long data 형식) - 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 = 80000001, 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%

(모든 결과는 세 번의 실행에 대한 사용자 초의 평균이며, 실행 간의 시간 변화 < 10%입니다.

혼합 결과

정수 범위에 있을 때 C 및 Java 데이터 유형을 정수로 변경하면 실행 속도가 크게 빨라집니다.BBB와 Cloud9 컴퓨터에서 int로 전환하면 node.js보다 C가 더 빠릅니다.하지만 제 Mac에서는 node.js 프로그램이 더 빨리 실행되었습니다.아마도 맥은 클랑을 사용하고 있고 BBB와 클라우드 9 컴퓨터는 gcc를 사용하고 있기 때문일 것입니다.gcc가 gcc보다 더 빠른 프로그램을 컴파일하는지 아는 사람?

64비트 정수 C를 모두 사용했을 때는 BBB와 Cloud9 PC에서는 node.js보다 조금 빨랐지만 MAC에서는 조금 느렸습니다.

또한 이 테스트에서 파이피가 표준 파이썬보다 약 4배 빠르다는 것을 알 수 있습니다.

node.js가 C와 호환된다는 사실이 놀랍습니다.

저는 며칠 동안 V8 엔진에서 발생하는 Hydrogen IR을 중심으로 JS/V8과 C의 성능 차이를 조사했습니다.하지만, 특별한 최적화 기능이 없다는 것을 확인한 후, 조립 결과 분석으로 돌아갔고, 그 답이 매우 간단하다는 것을 알게 되었습니다. V8의 내부 자료에 대한 Jay Conrod의 블로그 게시물에 있는 몇 개의 문장으로 요약됩니다.

스펙에 따르면 자바스크립트의 모든 숫자는 64비트 부동 소수점 두 배입니다.우리는 정수를 자주 사용하기 때문에 V8은 가능때마다 31비트 부호 정수로 숫자를 표시합니다.

당면한 예제는 모든 계산을 32비트로 맞출 수 있으며 node.js는 이를 최대한 활용합니다!C 코드는 다음을 활용합니다.longOP(나의 플랫폼뿐만 아니라) 플랫폼에서 64비트 유형이 되는 유형입니다.따라서 32비트 산술 대 64비트 산술 문제인데, 대부분 비용이 많이 드는 분할/잔여 연산 때문입니다.

한다면longC 코드는 다음으로 대체됩니다.int, 그러면 gcc가 node.js를 능가하는 바이너리.

또한 루프가 32비트 숫자의 영역 밖에 있는 범위에서 소수를 찾도록 만들어지면 node.js 버전의 성능이 크게 떨어집니다.


증명

사용된 소스 코드는 결과 아래의 답변에서 더 확인할 수 있습니다.

C와 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

부호화된 32비트 정수의 한계 부근에서의 성능 수치

첫 번째 열에 포함된 숫자로 시작하는 길이 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);
}

새 알고리즘으로 파이썬에서 코드를 실행했을 때:

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

위의 C 벤치마크보다 더 빠릅니다.언어가 단순하면 알고리즘을 더 잘 설계할 수 있다고 생각하지만, 그것이 제 생각입니다. (또한 다중 처리를 사용하여 훨씬 더 빠르게 만들 수 있습니다.)

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

언급URL : https://stackoverflow.com/questions/39360403/how-can-node-js-be-faster-than-c-and-java-benchmark-comparing-node-js-c-java

반응형