I made a very simple benchmarking program that calculates all the prime numbers up to 10,000,000 in 4 different languages.
above is average of 3 runs each, user time
Node.js runs by far the fastest. This is confusing to me for two reasons:
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();
$ 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; }
$ gcc primeCalc.c -lm -g -O2 -std=c99 -Wall $ time ./a.out total 664579 real 0m6.718s user 0m6.668s sys 0m0.008s
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; }; }
$ 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)
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
(6.66 s) optimization 2, gcc primeCalc.c -lm -O2 -std=c99 -Wall
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 }
Update 03/15/2017 After reading the answer from @leon I ran a few verification tests.
Test 1 - 32 Bit Beaglebone Black, 664,579 primes up to 10,000,000
Unedited calcPrime.js and calcPrime.c running on the Beaglebone black which has a 32 bit processor.
Test 2 - 64 Bit Macbook Pro, 664,579 primes up to 10,000,000
Replace long datatypes in calcPrime.c code with uint32_t and running on my MacBook pro which has a 64 bit processor.
Test 3 - 64 Bit Macbook Pro, 91,836 primes (i=1; i < 8,000,000,000; i+=10000)
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)
Test 4 - 64 Bit Macbook Pro, 86,277 primes (i = 8,000,00,001; i < 16,000,000,000; i+=10000)
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)
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%
(All results are the average of the user seconds for three runs, time variation between runs < 10%)
Mixed Results
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?
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.
You can also see that pypy is about four times faster than standard python in these tests.
The fact that node.js is even compatible to C amazes me.
With the help of event-driven, non-blocking architecture, Nodejs can process multiple requests at a time and speeds up code execution. Between Nodejs vs Python performance, Node allows you to code outside the web browser using TCP sockets, making it more resource-efficient.
js is faster than Java because it uses on non-blocking calls (not just non-blocking I/O) while Java web apps usually rely on multi-threading.
c to use a double instead of a long in the loop, the time C took was even longer! Not trying to start a flame war, but why is Node. JS (116 ms.) so much faster than native C (198 ms.)
The virtual machine can take the source code to compile it into the machine code at runtime. What it means is that all the “hot” functions that get called often than not can be compiled to the machine code thus boosting the execution speed.
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:
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.
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.
If long
in the C code is replaced with int
, then the binary produced by gcc beats 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.
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
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); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With