Как node.js может быть быстрее c и java? Сравнительный пример: node.js, c, java и python

Я сделал очень простую программу бенчмаркинга, которая вычисляет все простые числа до 10 000 000 на 4 разных языках.

  • (2,97 секунды) – node.js (javascript) (4.4.5)
  • (6,96 секунды) – c (c99)
  • (6,91 секунды) – java (1.7)
  • (45,5 секунд) – python (2.7)

выше – среднее значение 3 пробега каждый, время пользователя

Node.js работает намного быстрее. Это меня путает по двум причинам:

  1. javascript всегда использует float с двойной точностью для переменных, тогда как c и java используют (long) целые числа в этом случае. Математика с целыми числами должна быть быстрее.
  2. javascript часто упоминается как интерпретируемый, когда на самом деле это просто компилируемый язык во времени. Но даже так, как компилятор JIT может быть быстрее, чем полностью скомпилированный язык? Код python работает медленнее, что не удивительно, но почему код node.js не работает с такой же скоростью, как у python?

Я скомпилировал код c с оптимизацией -O2, но я попробовал его со всеми уровнями оптимизации, и это не создало заметной разницы.

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

 $ 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 результаты

 $ 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

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-результаты

  $ 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

 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 

дополнительные c время выполнения (добавление)

  • (7.81 с) без оптимизации, gcc primeCalc.c -lm -std = c99 -Wall
  • (8.13 с) оптимизация 0, gcc primeCalc.c -lm -O0 -std = c99 -Wall
  • (7.30 с) оптимизация 1, gcc primeCalc.c -lm -O1 -std = c99 -Wall
  • (6.66 с) оптимизация 2, gcc primeCalc.c -lm -O2 -std = c99 -Wall

    • среднее из 3 новых запуска каждого пользовательского времени уровня оптимизации *

Я прочитал сообщение здесь: Почему этот NodeJS 2x быстрее, чем родной C? В этом коде используется пример, который на самом деле ничего не делает. Это похоже на то, что компилятор может определить результат во время компиляции, и даже не нужно выполнять цикл 100000000 раз, чтобы придумать ответ. Если добавить еще один шаг разделения к расчету, оптимизация намного менее значительна.

 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 бит Beaglebone Black, 664 579 простых чисел до 10 000 000

Unedited calcPrime.js и calcPrime.c работает на черном Beaglebone, который имеет 32-битный процессор.

  • C код = 62 секунды (gcc, длинный тип данных)
  • JS-код = 102 секунды (узел v4)

Тест 2 – 64 бит Macbook Pro, 664 579 простых чисел до 10 000 000

Замените длинные типы данных в файле calcPrime.c с помощью uint32_t и запустите на моем MacBook pro, который имеет 64-битный процессор.

  • C код = 5,73 секунды (clang, long datatype)
  • C код = 2.43 секунды (clang, uint_32_t тип данных)
  • JS-код = 2.12 секунды (узел v4)

Тест 3 – 64 бит Macbook Pro, 91 836 простых чисел (i = 1; i <8 000 000 000, i + = 10000)

Используйте unsigned long datatypes в 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)

Используйте unsigned long datatypes в коде 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% 

Тест 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%)

Смешанные результаты

Изменение типа данных C и Java на целое число, когда в диапазоне целых чисел значительно ускорило выполнение. На компьютерах BBB и Cloud9 переключение на int с C происходит быстрее, чем node.js. Но на моем Mac программа node.js по-прежнему работает быстрее. Возможно, потому, что Mac использует clang, а компьютеры BBB и Cloud 9 используют gcc. Кто-нибудь знает, если gcc компилирует более быстрые программы, чем gcc?

При использовании всех 64-битных целых чисел C был немного быстрее, чем node.js на BBB и Cloud9 PC, но немного медленнее на моем MAC.

Вы также можете увидеть, что в этих тестах pypy примерно в четыре раза быстрее, чем стандартный python.

Тот факт, что node.js даже совместим с C, поражает меня.

  • Получить активное окно с помощью Python
  • Threading в Django не работает в производстве
  • как преобразовать открытый ключ base64 / radix64 в формат pem в python
  • Как добавить «3 месяца» к объекту datetime.date в python?
  • Дизайн виртуальной пробной комнаты
  • Порядок итераций на Python варьируется от run to run
  • Как преобразовать мой bytearray ('b \ x9e \ x18K \ x9a') в нечто подобное -> '\ x9e \ x18K \ x9a' <--- просто str, а не массив
  • Тест Django FileField с использованием тестовых приборов
  • One Solution collect form web for “Как node.js может быть быстрее c и java? Сравнительный пример: node.js, c, java и python”

    Я провел пару дней, исследуя разницу в производительности между JS / V8 и C, фокусируясь в первую очередь на IR водорода, генерируемом двигателем V8. Однако, убедившись, что в нем нет экстраординарных оптимизаций, я вернулся к анализу сборки, и мне показалось, что ответ был очень простым, и он сводился к двум предложениям в блоге 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 значительно падает.


    доказательство

    Используемый исходный код можно найти в ответе ниже результатов.

    Подсчет простых чисел менее 10 миллионов с помощью 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); } 
    Interesting Posts

    Открытый модуль Python os открывает выше существующий каталог с относительным путем

    Эффективный и точный расчет евклидова расстояния

    Как создать декоратор класса Python, способный обернуть экземпляр, класс и статические методы?

    подклассы объекта pandas работают иначе, чем подкласс другого объекта?

    Идентификатор клиентского ключа Google App Engine NDB

    Как создать динамические переменные в Python?

    Buildozer: вручную включить внешний API

    Возврат строки из CSV, если указанное значение в строке соответствует условию

    данные биннинга в python с scipy / numpy

    Как добавить изображение в Tkinter (Python 2.7)

    исключение catch для python и продолжить попытку блокировки

    Невозможно перебрать макросы VBA из Python

    отправка данных как объекта JSON из Python в Javascript с Jinja

    Питонический способ определения, является ли текущий элемент первым или последним элементом генератора?

    Шаблон Regex для обработки как чувствительных к регистру, так и без учета регистра в одном выражении

    Python - лучший язык программирования в мире.