Tim*_*ann 14 c python java node.js
我做了一个非常简单的基准测试程序,用4种不同语言计算高达10,000,000的所有素数.
以上是每次平均3次运行,用户时间
Node.js运行速度最快.这让我很困惑,原因有两个:
我用-O2优化编译了c代码,但我尝试了所有级别的优化,并没有产生显着的差异.
"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();
Run Code Online (Sandbox Code Playgroud)
$ time node primeCalc.js
total 664579
real 0m2.965s
user 0m2.928s
sys 0m0.016s
$ node --version
v4.4.5
Run Code Online (Sandbox Code Playgroud)
#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;
}
Run Code Online (Sandbox Code Playgroud)
$ gcc primeCalc.c -lm -g -O2 -std=c99 -Wall
$ time ./a.out
total 664579
real 0m6.718s
user 0m6.668s
sys 0m0.008s
Run Code Online (Sandbox Code Playgroud)
公共类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;
};
}
Run Code Online (Sandbox Code Playgroud)
$ 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)
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
time python primeCalc.py
total 664579
real 0m46.588s
user 0m45.732s
sys 0m0.156s
$ python --version
Python 2.7.6
Run Code Online (Sandbox Code Playgroud)
$ 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
Run Code Online (Sandbox Code Playgroud)
(6.66 s)优化2,gcc primeCalc.c -lm -O2 -std = c99-Wall
我在这里阅读帖子: 为什么这个NodeJS比本机C快2倍? 此代码使用的实例实际上并没有做任何重要的事情.这就好像编译器可以在编译时计算出结果,甚至不需要执行循环100000000次来得出答案.如果在计算中添加另一个除法步骤,则优化不太重要.
for (long i = 0; i < 100000000; i++) {
d += i >> 1;
d = d / (i +1); // <-- New Term
}
Run Code Online (Sandbox Code Playgroud)
更新 03/15/2017阅读@leon的答案后,我进行了一些验证测试.
测试1 - 32位Beaglebone黑色,664,579素数高达10,000,000
未编辑的calcPrime.js和calcPrime.c在具有32位处理器的Beaglebone black上运行.
测试2 - 64位Macbook Pro,664,579素数高达10,000,000
使用uint32_t替换calcPrime.c代码中的长数据类型,并在具有64位处理器的MacBook pro上运行.
测试3 - 64位Macbook Pro,91,836个素数(i = 1; i <8,000,000,000; i + = 10000)
在C代码中使用无符号长数据类型,强制javascript使用64位. - C代码= 20.4秒(clang,长数据类型) - JS代码= 17.8秒(节点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,长数据类型) - 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%
Run Code Online (Sandbox Code Playgroud)
测试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%
Run Code Online (Sandbox Code Playgroud)
(所有结果均为三次运行的用户秒数的平均值,运行之间的时间差异<10%)
混合结果
在整数范围内将C和Java数据类型更改为整数会显着加快执行速度.在BBB和Cloud9计算机上切换到int使得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兼容的事实让我感到惊讶.
Leo*_*eon 21
我花了几天时间研究JS/V8和C之间的性能差异,首先关注V8发动机产生的氢气IR.然而,在确定那里没有非凡的优化之后,我回到了汇编输出的分析,它让我觉得答案是一个非常简单的答案,在Jay Conrod关于内部的博客文章中沸腾到几句话 V8:
根据规范,JavaScript中的所有数字都是64位浮点双精度数.我们经常使用整数,因此V8尽可能表示带有31位有符号整数的数字.
手边的示例允许以32位和node.js拟合所有计算,充分利用它!C代码使用的long
类型在OP(以及我的)平台上恰好是64位类型.因此,它是32位算术与64位算术问题,主要是由于昂贵的除法/余数运算.
如果long
在C代码中替换为int
,则由gcc生成的二进制文件击败node.js.
此外,如果循环用于在32位数字范围之外的范围内查找质数,则node.js版本的性能会显着下降.
在结果下面的答案中进一步找到使用的源代码.
使用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
Run Code Online (Sandbox Code Playgroud)
性能数字在有符号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
-----------------------------------
Run Code Online (Sandbox Code Playgroud)
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');
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)