node.js如何比c和java更快?基准比较node.js,c,java和python [英] How can node.js be faster than c and java? Benchmark comparing node.js, c, java and python
问题描述
我做了一个非常简单的基准测试程序,用4种不同语言计算高达10,000,000的所有素数。
I made a very simple benchmarking program that calculates all the prime numbers up to 10,000,000 in 4 different languages.
- (2.97秒) - node.js(javascript)(4.4.5)
- ( 6.96秒) - c(c99)
- (6.91秒) - java(1.7)
- (45.5秒) - python(2.7)
- (2.97 seconds) - node.js (javascript) (4.4.5)
- (6.96 seconds) - c (c99)
- (6.91 seconds) - java (1.7)
- (45.5 seconds) - python (2.7)
以上是平均每次3次运行,用户时间
Node.js运行速度最快。这让我感到困惑有两个原因:
Node.js runs by far the fastest. This is confusing to me for two reasons:
- javascript总是使用双精度浮点数作为变量而c和java正在使用(长)整数这个案例。带整数的数学应该更快。
- javascript通常被称为解释,实际上它是一种及时编译的语言。但即便如此,JIT编译器如何比完全编译的语言更快?
python代码运行速度最慢,这并不奇怪,但为什么node.js代码的运行速度与python类似?
我用-O2优化编译了c代码,但我尝试了所有级别的优化,并没有产生显着的差异。
I compiled the c code with -O2 optimization, but I tried it with all levels of optimization and it didn't make a noticeable difference.
"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 results
node.js results
$ time node primeCalc.js
total 664579
real 0m2.965s
user 0m2.928s
sys 0m0.016s
$ node --version
v4.4.5
primeCalc.c
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结果
c results
$ 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 {
PrimeCalc.java
public class 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结果
java results
$ 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
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 results
python results
time python primeCalc.py
total 664579
real 0m46.588s
user 0m45.732s
sys 0m0.156s
$ python --version
Python 2.7.6
linux
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秒)没有优化,gcc primeCalc.c -lm -std = c99 -Wall
- (8.13 s)优化0,gcc primeCalc.c -lm -O0 -std = c99 - Wall
- (7.30秒)优化1,gcc primeCalc.c -lm -O1 -std = c99 -Wall
-
(6.66 s )优化2,gcc primeCalc.c -lm -O2 -std = c99 -Wall
additional c run times (addendum)
- (7.81 s) no optimization, gcc primeCalc.c -lm -std=c99 -Wall
- (8.13 s) optimization 0, gcc primeCalc.c -lm -O0 -std=c99 -Wall
- (7.30 s) optimization 1, gcc primeCalc.c -lm -O1 -std=c99 -Wall
(6.66 s) optimization 2, gcc primeCalc.c -lm -O2 -std=c99 -Wall
- 平均每次优化3次新运行离子水平用户时间*
我在这里阅读帖子:
为什么这个NodeJS 2x比原生C快?
此代码使用的实例实际上并没有做任何重要的事情。就好像编译器可以在编译时找出结果,甚至不需要执行循环100000000次来得出答案。
如果在计算中添加另一个除法步骤,则优化的重要性要小得多。I read the post here: Why is this NodeJS 2x faster than native C? This code uses an example that doesn't actually do anything significant. It's as if the compiler can figure out the result at compile time and it doesn't even need to execute the loop 100000000 times to come up with the answer. If one adds another division step to the calculation the optimization is much less significant.
for (long i = 0; i < 100000000; i++) { d += i >> 1; d = d / (i +1); // <-- New Term }
- ( 1.88秒)没有优化
- (1.53秒)优化(-O2)
- C代码= 62秒(gcc,长数据类型)
- JS代码= 102秒(节点v4)
- C代码= 5.73秒(clang,长数据类型)
- C代码= 2.43 s econds(clang,uint_32_t数据类型)
- JS代码= 2.12秒(节点v4)
更新03/15/2017
阅读@leon的答案后,我进行了一些验证测试。Update 03/15/2017 After reading the answer from @leon I ran a few verification tests.
测试1 - 32位Beaglebone Black,664,579素数高达10,000,000
未经编辑的calcPrime.js和calcPrime.c正在运行在具有32位处理器的Beaglebone black上。
Unedited calcPrime.js and calcPrime.c running on the Beaglebone black which has a 32 bit processor.
测试2 - 64位Macbook Pro,664,579素数高达10,000,000
使用uint32_t替换calcPrime.c代码中的长数据类型,并在具有64位处理器的MacBook pro上运行。
Replace long datatypes in calcPrime.c code with uint32_t and running on my MacBook pro which has a 64 bit processor.
测试3 - 64位Macbook Pro,91,836个素数(i = 1;我< 80亿; i + = 10000)
在C代码中使用无符号长数据类型,强制javascript使用64位。
- C代码= 20.4秒(clang,long数据类型)
- JS代码= 17.8秒(节点v4)Use unsigned long datatypes in C code, force javascript to use some 64 bit. - C code = 20.4 seconds (clang, long datatype) - JS code = 17.8 seconds (node v4)
测试4 - 64位Macbook Pro,86,277个素数(i = 8,000,00,001; i <16,000,000,000; i + = 10000)
在C代码中使用无符号长数据类型,强制javascript使用所有64位。
- C代码= 35.8秒(clang,long数据类型)
- JS代码= 34.1秒(节点v4)Use unsigned long datatypes in C code, force javascript to use all 64 bit. - C code = 35.8 seconds (clang, long datatype) - JS code = 34.1 seconds (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%
(所有结果均为用户的平均值秒数三次运行,运行之间的时间变化< 10%)
(All results are the average of the user seconds for three runs, time variation between runs < 10%)
混合结果
将C和Java数据类型更改为整数当在整数范围内显着加快执行速度。在BBB和Cloud9计算机上切换到int使得C比node.js更快。但是在我的Mac上,node.js程序仍然运行得更快。也许是因为Mac正在使用clang而BBB和Cloud 9计算机正在使用gcc。有没有人知道gcc是否编译比gcc更快的程序?
Changing the C and Java datatype to integer when in the range of integers significantly sped up execution. On the BBB and Cloud9 computers switching to ints made C faster than node.js. But on my Mac the node.js program still ran faster. Perhaps because the Mac is using clang and the BBB and Cloud 9 computers are using gcc. Does anyone know if gcc compiles faster programs than gcc?
当使用所有64位整数时,C比BBB和Cloud9 PC上的node.js快一点但有点快在我的MAC上慢一点。
When using all 64 bit integers C was a bit faster than node.js on the BBB and Cloud9 PC but a little slower on my MAC.
在这些测试中,您还可以看到pypy比标准python快四倍。
You can also see that pypy is about four times faster than standard python in these tests.
node.js甚至与C兼容的事实让我感到惊讶。
The fact that node.js is even compatible to C amazes me.
推荐答案
我花了几天时间调查JS / V8和C之间的性能差异,首先集中在V8发动机产生的氢气IR上。然而,在确保那里没有非常优化之后,我回到了对程序集输出的分析,它让我觉得答案很简单,在 Jay Conrod关于V8内幕的博客文章:
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:
根据规范,JavaScript中的所有数字都是64位浮点数双精度数。我们经常使用整数,因此 V8表示尽可能使用31位有符号整数的数字 。
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位算术问题,主要是由于昂贵的除法/余数运算。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.如果
long < C代码中的/ code>替换为
int
,然后由gcc生成的二进制文件beats node.js。If
long
in the C code is replaced withint
, then the binary produced by gcc beats node.js.此外,如果循环用于查找超出32位数字范围的质数,则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.
在结果下方的答案中可以找到使用过的源代码。
The used source code is found further in the answer, below the results.
使用C和node.js计算小于1000万的素数
$ 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的素数:
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); }
这篇关于node.js如何比c和java更快?基准比较node.js,c,java和python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!