Ich habe ein sehr einfaches Benchmarking-Programm erstellt, das alle Primzahlen bis 10.000.000 in 4 verschiedenen Sprachen berechnet.
- (2,97 Sekunden) – node.js (Javascript) (4.4.5)
- (6,96 Sekunden) – c (c99)
- (6,91 Sekunden) – Java (1,7)
- (45,5 Sekunden) – Python (2,7)
oben ist der Durchschnitt von jeweils 3 Läufen, Benutzerzeit
Node.js läuft mit Abstand am schnellsten. Das verwirrt mich aus zwei Gründen:
- Javascript verwendet immer Floats mit doppelter Genauigkeit für Variablen, während C und Java in diesem Fall (lange) Ganzzahlen verwenden. Mathematik mit ganzen Zahlen sollte schneller sein.
- Javascript wird oft als interpretiert bezeichnet, obwohl es sich tatsächlich um eine just in time kompilierte Sprache handelt. Aber wie kann der JIT-Compiler trotzdem schneller sein als eine vollständig kompilierte Sprache? Der Python-Code läuft am langsamsten, was keine Überraschung ist, aber warum läuft der node.js-Code nicht mit einer ähnlichen Geschwindigkeit wie der Python?
Ich habe den C-Code mit -O2-Optimierung kompiliert, aber ich habe es mit allen Optimierungsstufen versucht und es hat keinen merklichen Unterschied gemacht.
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-Ergebnisse
$ time node primeCalc.js
total 664579
real 0m2.965s
user 0m2.928s
sys 0m0.016s
$ node --version
v4.4.5
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 Ergebnisse
$ 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
öffentliche Klasse 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-Ergebnisse
$ 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-Ergebnisse
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
zusätzliche c Laufzeiten (Nachtrag)
- (7,81 s) keine Optimierung, gcc primeCalc.c -lm -std=c99 -Wall
- (8,13 s) Optimierung 0, gcc primeCalc.c -lm -O0 -std=c99 -Wall
- (7,30 s) Optimierung 1, gcc primeCalc.c -lm -O1 -std=c99 -Wall
-
(6,66 s) Optimierung 2, gcc primeCalc.c -lm -O2 -std=c99 -Wall
- durchschnittlich 3 neue Läufe pro Optimierungsstufe Benutzerzeit *
Ich habe den Beitrag hier gelesen: Warum ist dieses NodeJS 2x schneller als natives C? Dieser Code verwendet ein Beispiel, das eigentlich nichts Bedeutendes bewirkt. Es ist, als könnte der Compiler das Ergebnis zur Kompilierzeit herausfinden und muss die Schleife nicht einmal 100000000 Mal ausführen, um die Antwort zu erhalten. Fügt man der Rechnung einen weiteren Teilungsschritt hinzu, fällt die Optimierung viel weniger ins Gewicht.
for (long i = 0; i < 100000000; i++) {
d += i >> 1;
d = d / (i +1); // <-- New Term
}
- (1,88 Sekunden) ohne Optimierung
- (1,53 Sekunden) mit Optimierung (-O2)
Update 15.03.2017
Nachdem ich die Antwort von @leon gelesen hatte, führte ich einige Verifizierungstests durch.
Test 1 – 32 Bit Beaglebone Black, 664.579 Primzahlen bis zu 10.000.000
Unbearbeitete calcPrime.js und calcPrime.c laufen auf dem Beaglebone Black, der einen 32-Bit-Prozessor hat.
- C-Code = 62 Sekunden (gcc, langer Datentyp)
- JS-Code = 102 Sekunden (Knoten v4)
Test 2 – 64 Bit Macbook Pro, 664.579 Primzahlen bis zu 10.000.000
Ersetzen Sie lange Datentypen im calcPrime.c-Code durch uint32_t und laufen Sie auf meinem MacBook Pro mit einem 64-Bit-Prozessor.
- C-Code = 5,73 Sekunden (Klang, langer Datentyp)
- C-Code = 2,43 Sekunden (clang, uint_32_t-Datentyp)
- JS-Code = 2,12 Sekunden (Knoten v4)
Test 3 – 64 Bit Macbook Pro, 91.836 Primzahlen (i=1; i < 8.000.000.000; i+=10000)
Verwenden Sie unsignierte lange Datentypen in C-Code, erzwingen Sie Javascript, um 64 Bit zu verwenden. – C-Code = 20,4 Sekunden (Clang, langer Datentyp) – JS-Code = 17,8 Sekunden (Knoten v4)
Test 4 – 64 Bit Macbook Pro, 86.277 Primzahlen (i = 8.000.00.001; i < 16.000.000.000; i+=10000)
Verwenden Sie unsignierte lange Datentypen in C-Code, erzwingen Sie Javascript, alle 64 Bit zu verwenden. – C-Code = 35,8 Sekunden (Clang, langer Datentyp) – JS-Code = 34,1 Sekunden (Knoten v4)
Test 5 – Cloud9 64-Bit 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%
Test 6 – Cloud9 64-Bit 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%
(Alle Ergebnisse sind der Durchschnitt der Benutzersekunden für drei Läufe, Zeitabweichung zwischen Läufen < 10 %)
Gemischte Resultate
Das Ändern des C- und Java-Datentyps in Integer im Bereich von Integern beschleunigte die Ausführung erheblich. Auf den BBB- und Cloud9-Computern machte der Wechsel zu ints C schneller als node.js. Aber auf meinem Mac lief das Programm node.js immer noch schneller. Vielleicht, weil der Mac clang verwendet und die BBB- und Cloud 9-Computer gcc verwenden. Weiß jemand, ob gcc Programme schneller kompiliert als gcc?
Bei Verwendung aller 64-Bit-Integer war C etwas schneller als node.js auf dem BBB- und Cloud9-PC, aber etwas langsamer auf meinem MAC.
Sie können auch sehen, dass pypy in diesen Tests etwa viermal schneller ist als Standard-Python.
Dass node.js sogar zu C kompatibel ist, erstaunt mich.
v8 ist optimieren. In der Tat, v8 ist sehr optimieren. Jedoch, in acht nehmen Mikro-Benchmarks.
– Elliot Frisch
7. September 2016 um 2:39 Uhr
vielleicht legst du mit dem Finger auf etwas
long
zudouble
Konvertierung, diese könnten auf lange Sicht kostspielig sein– dvhh
7. September 2016 um 2:41 Uhr
Mögliches Duplikat von Warum ist dieses NodeJS 2x schneller als natives C?
– EnRaiser
7. September 2016 um 2:44 Uhr
@enRaiser Hier,
gcc
wird bestanden-O2
schon.– Elliot Frisch
7. September 2016 um 2:47 Uhr
-O2 -m64 senkte den Durchschnitt bei drei Läufen von 6,66 auf 6,41 Sekunden, aber die Ergebnisse variieren jedes Mal um ein paar Zehntelsekunden, sodass ich nicht sagen kann, dass es tatsächlich ein signifikantes Ergebnis hatte.
– Timothy Vann
7. September 2016 um 3:24 Uhr