2014-12-11 2 views
5

Ради презентации на работе, я хотел бы сравнить производительность NodeJS на C. Вот что я писал:Почему этот NodeJS 2x быстрее, чем родной C?

Node.js (for.js):

var d = 0.0, 
    start = new Date().getTime(); 

for (var i = 0; i < 100000000; i++) 
{ 
    d += i >> 1; 
} 

var end = new Date().getTime(); 

console.log(d); 
console.log(end - start); 

C (for.c)

#include <stdio.h> 
#include <time.h> 

int main() { 
    clock_t start = clock(); 

    long d = 0.0; 

    for (long i = 0; i < 100000000; i++) 
    { 
    d += i >> 1;  
    } 

    clock_t end = clock(); 
    clock_t elapsed = (end - start)/(CLOCKS_PER_SEC/1000); 

    printf("%ld\n", d); 
    printf("%lu\n", elapsed); 
} 

Использование GCC Я собирал мой for.c и побежал:

gcc for.c 
./a.out 

Результаты:

2499999950000000 
198 

Тогда я попытался его в NodeJS:

node for.js 

Результаты:

2499999950000000 
116 

После запуска несколько раз, я обнаружил, что это не самое относится ни на что. Если я переключил for.c на использование double вместо long в цикле, время C заняло еще больше!

Не пытайтесь начать пламенную войну, но почему Node.JS (116 мс.) Намного быстрее, чем нативный C (198 мс) при выполнении этой же операции? Использует ли Node.JS оптимизацию, которую GCC не делает из коробки?

EDIT:

За предложение в комментариях, я побежал gcc -Wall -O2 for.c. Результаты улучшились до C с 29 мс. Это задает вопрос, как настраиваемые параметры C не оптимизируются, как компилятор Javascript? Также, что -Wall и -02 делают. Мне действительно интересно узнать, что здесь происходит.

+1

Попробуйте выполнить компиляцию с 'gcc -Wall -O2 for.c' и снова проверить. Использование 'gcc' (или' clang') без передачи какого-либо явного флага оптимизации для бенчмаркинга бесполезно! –

+1

Вы пытались явно установить разные уровни оптимизации при компиляции программы на C? – mscdex

+1

'long d = 0.0;' - не 'long' целочисленный тип? – usr2564301

ответ

13

Это задает вопрос, как настраиваются собственные настройки C так же сильно, как Javascript-компилятор?

Поскольку C статически скомпилирован и связан, что требует потенциально длинного шага сборки всей вашей кодовой базы (я когда-то работал в одном, который занимал почти час для полной оптимизированной сборки, но всего 10 минут в противном случае) и очень опасный язык аппаратного уровня, который подвергает риску много неопределенного поведения, если вы не относитесь к нему с осторожностью, настройки компиляторов по умолчанию обычно не оптимизируются для сглаживания, поскольку это сборка разработчика/отладки, предназначенная для помощи в отладке и производительности с более быстрым поворотом.

Так что в C вы получите четкое разделение между неоптимизированным но быстрее к сборке, проще в отладку разработчика/отладке сборки и очень оптимизирован, медленнее в сборку, сложнее в отладке производство/выпуск сборка выполняется очень быстро, а настройки компиляторов по умолчанию часто предпочитают прежние.

С чем-то вроде v8/NodeJS, вы имеете дело с компилятором «точно в срок» (динамическая компиляция), который строит и оптимизирует только необходимый код «на лету» во время выполнения. Кроме того, JS является гораздо более безопасным языком, а также часто предназначен для обеспечения безопасности, что не позволяет работать с исходными битами и байтами аппаратного обеспечения.

В результате этого не требуется такое сильное разблокирование выпуска/отладки встроенного, статически скомпилированного языка, такого как C/C++. Но это также не позволяет вам поставить педаль на металл, как вы можете на C, если вы действительно этого хотите.

Многие люди, пытающиеся сравнивать C/C++ с другими языками, часто не понимают этого различия в построении и важность настроек оптимизации компилятора/компоновщика и путают. Как вы можете видеть, при правильных настройках трудно превзойти производительность этих родных компиляторов и языков, которые позволяют писать действительно низкоуровневый код.

2

Добавление ключевого слова регистра помогает, как и ожидалось

#include <stdio.h> 
#include <time.h> 

int main() { 
    register long i, d; 
    clock_t start = clock(); 
    i = d = 0L; 

    for (i = 0; i < 100000000L; i++) { 
    d += i >> 1; 
    } 

    clock_t end = clock(); 
    clock_t elapsed = (end - start)/(CLOCKS_PER_SEC/1000); 

    printf("%ld\n", d); 
    printf("%lu\n", elapsed); 
} 

и компилировать с помощью компилятора C

cc  for.c -o for 

./for ; node for.js 

возвращается

2499999950000000 
97 
2499999950000000 
222 
+0

Интересно, что я недавно читал, что 'register' больше, однако, не намного лучше любого из опций' -OX' , Но это имеет смысл. Хороший пост. – JoshJ

1

Я также сделал некоторые тесты вычисления простых чисел и я обнаружил, что Node.js примерно в два раза быстрее, чем C see here.

Если у вас очень простой тип подсчета, то оптимизация -O2 может иногда преобразовывать вывод в простую формулу, даже не повторяя цикл. См. Karl's Blog для объяснения. Если вы добавите что-то более сложное в рутину, скорее всего, node.js будет быстрее. например, я добавил термин «devisor» в вашу примерную программу, и оптимизация c -O2 больше не могла преобразовать его в простую формулу, а node.js снова стал быстрее.

Я все еще не понимаю, как node.js может быть заклинателем, чем c, при простых вычислениях целых чисел, но в каждом тесте, который я сделал до сих пор, он быстрее. Я также провел несколько тестов с побитовыми вычислениями, которые я еще не опубликовал, и все еще node.js был быстрее.

Смежные вопросы