Как стать автором
Обновить

Комментарии 35

А сишное скомпилировано в release?
Да, release.
А как на счёт операций с памятью? С Си можно контролировать когда и как выделяешь память, в динамических языках в общем случае нельзя, или это можно но это будех хак (приходится думать на уровень ниже). Тут то у Си и будет выигрыш в производительности.

Например если в цикле мы создаём много разных объектов разных типов или строки, хэши массивы. В си можно один раз под это выделить буфер и создать пару структур. В динамических языках придётся выделять память внутри цикла.
мозговзрывательная статья в плане результатов тестов. очень ценный опыт.
Я сделал для себя такой вывод: вызов нативного модуля в V8 — дорогое удовольствие, а не «JS такой же быстрый, как и C++».
это была моя первая мысль
потом возник вопрос — почему тогда при больших итерациях нативный код начинает отставать, когда должно быть наоборот.
собака зарыта в обертке вызова V8, что довольно-таки накладно.
сделайте тест нативного исполнения расчета двойного интеграла и сравните с тестом 2 (нативное исполнение)

Все эти загогулинки мы проходили, когда эксперементировали с написанием нативных вызовах РНР.
функция вызывается один раз и выполняется сотни миллисекунд. в таком случае накладные расходы на пересечение границы просто незаметны. они ведь отнюдь не десятками миллисекунд измеряются, а совсем даже долями миллисекунды.
однако факт остается фактом…
какой факт? что замеры производительности С++ кода на разных машинах, компиляторах и флагах сборки дали разные результаты? это действительно факт.

обратите внимание, я не пытаюсь утверждать, что V8 производит в данном конкретном случае супер код (хотя я посмотрел на него и код достаточно хороший, если не считать бага описнного внизу) или что пересечение границы JS-C++ легче перышка. я утверждаю другое: разница в десятки миллисекунд на таком коде не может быть объяснена задержками при пересечении этой границы.

более того, я даже склоняюсь ко мнению, что у автора что-то со флагами компиляции не так — из-за чего скорость С++ного кода оказалась такой странной. но для подтверждения надо посмотреть на то, что его компилятор породил в этом месте. (еще заметьте, что и время исполнения обычного С++ного варианта и CUDA-вского включают в себя время пересечения границы JS-C++, т.е. оно меньше 17ms).

какой факт? что замеры производительности С++ кода на разных машинах, компиляторах и флагах сборки дали разные результаты? это действительно факт.

обратите внимание, я не пытаюсь утверждать, что V8 производит в данном конкретном случае супер код (хотя я посмотрел на него и код достаточно хороший, если не считать бага описнного внизу) или что пересечение границы JS-C++ легче перышка. я утверждаю другое: разница в десятки миллисекунд на таком коде не может быть объяснена задержками при пересечении этой границы.

более того, я даже склоняюсь ко мнению, что у автора что-то со флагами компиляции не так — из-за чего скорость С++ного кода оказалась такой странной. но для подтверждения надо посмотреть на то, что его компилятор породил в этом месте. (еще заметьте, что и время исполнения обычного С++ного варианта и CUDA-вского включают в себя время пересечения границы JS-C++, т.е. оно меньше 17ms).

«Если вы не умеете писать качественный код на C++ с низкоуровневой оптимизацией, то нет смысла пытаться написать нативное расширение для ускорения производительности.» — вот с этим согласен. Тут так и хочется взять что то типа liboil или накрайняк самим на SSE перенести, гарантирую 4x прирост.
Интересно было бы узнать время С++ версии, запущенной напрямую, а не через Node.js.
и я о том же… автор забыл это упомянуть.
вообще-то автор сделал не правильный вывод.
НЛО прилетело и опубликовало эту надпись здесь
Результаты меня поразили. Поэтому я решил проверить на других языках, после чего вообще впал в ступор.
Функция: integrate(-4,4,-4,4,1024);
Perl result = 127.999997360281
Perl time = 5295.28713226318

PHP result = 127.99999736028
PHP time = 3503.4229755402

JS result = 127.99999736028109
JS time = 550

Вот ещё тест на чистом С, без js оберток.
C result = 127.999997
C time = 210.000000
производительнее в 2 раза -впечатляет, спасибо
Впечатляет то что нода на данном тесте прозводительнее пела почти в 10 раз, и произвидительнее пхп в 6 раз.
Хотя в простом тесте на сетевые соединения (получить коннект, ответить, отвалиться), перл обгонял ноду примерно на 40%.
тесты скорости лучше сопровождать исходниками

+ у Perl есть PDL для манипуляций с числовыми данными:
use strict;
use PDL;
use Benchmark qw(:all :hireswallclock);
timethis(1, sub { print integrate(-4,4,-4,4,1024)."\n" });

sub integrate {
    my ($x0,$x1,$y0,$y1,$steps) = @_;
    my $d = zeroes(float, $steps, $steps);
    my ($x, $y) = ( $d->xlinvals($x0, $x1), $d->ylinvals($y0, $y1) );
    sum(sin($x * $y) / (1 + sqrt($x**2 + $y**2)) + 2) * ($x1-$x0) * ($y1-$y0) / $steps**2;
}


на Atom n400 ~ 0.8sec
Стоит сказать, что это не чистый перл, а насколько помню, в pdl используются так же C библиотеки?
Ну и у меня результат для вашего теста следующий:
Perl PDL result = 128
Perl PDL time = 1171.44012451172

Что тоже дольше чем для чистого JS.
А если интересны исходники на которых тестил, то залил на гитхаб github.com/Mikxail/perl_vs_node
да, pdl — xs модуль на cpan, т.ч. «cpan PDL» или, если debian, то «apt-get install pdl» )
судя по скорости nodejs, он явно хорошо предпроцессит чистый js в чтото иное, отчасти скомпилированное

p.s. запулил на гитхабе альтернативный вариант на perl без PDL — получилось ~ в 2.5 раз быстрее
Когда я писал этот тест, мне не хотелось делать различий от js. Понятно что тест нужно написать как вы запулили, но это будет отличаться от жс. Возможно nodejs сам оптимизирует до такой конструкции, поэтому и получается быстрее.
Но даже в новом, оптимизированном, варианте на перле, всё равно скорость отстает от скорости на ноде.
Так что остается порадоваться за производительность математических вычислений в v8. Но при этом желать большей производительности на сокетах.
думаю, ваш perl код медленее из-за 3-х объявлений «my» на каждом шаге интегрирования.

nodejs быстр, т.к. — developers.google.com/v8/design#mach_code
V8 compiles JavaScript source code directly into machine code when it is first executed. There are no intermediate byte codes, no interpreter.
думаю, ваш perl код медленее из-за 3-х объявлений «my» на каждом шаге интегрирования.

Не совсем так. Объявления переменных на каждой итерации конечно замедляет код, но не сильно, разница всего 200мс.
Perl(preinit vars) result = 127.999997360281
Perl(preinit vars) time = 4794.22998428345

Perl result = 127.999997360281
Perl time = 4931.83994293213
нужно приводить результаты тестов с одного железа, ибо от этого сильно зависит результат.
а где результаты Си?
пока писал — результаты уже замерили
у меня на ноде v0.8.8 вылазит баг в V8ом выводе представлений (representation inference) из-за которого замена на стэке (on stack replacement) пораждает совершенно бредовый код, в котором он пытается привести заведомо не целое значение к целому из-за чего происходит деоптимизация… в результате код постоянно крутится в неоптимизированной версии.

если же я запрещаю OSR и вызываю integrateJS дважды, то картина с пиковой производительностью получается примерно такая:

omega ~/src/temp/perl_vs_node ∳ git diff [master@368da] diff --git a/integral.js b/integral.js index 7b11f22..194adb2 100755 --- a/integral.js +++ b/integral.js @@ -18,4 +18,5 @@ function integrateJS(x0,xN,y0,yN,iterations){ console.log("JS time = "+(new Date().getTime() - time)); } -integrateJS(-4,4,-4,4,1024); \ No newline at end of file +integrateJS(-4,4,-4,4,1024); +integrateJS(-4,4,-4,4,1024); omega ~/src/temp/perl_vs_node ∳ node integral.js [master@368da] JS result = 127.99999736028109 JS time = 132 JS result = 127.99999736028109 JS time = 115 omega ~/src/temp/perl_vs_node ∳ node --nouse-osr integral.js [master@368da] JS result = 127.99999736028109 JS time = 122 JS result = 127.99999736028109 JS time = 55

баг я зарапортую на эту тему. причина по моей прикидке в том, что начальное значение переменной result — 0 выглядит как целое число и попадая в phi-функцию на границе между нормальным потоком управления и OSR-entry блоком приводит к неправильному выводу представления для этой phi (https://github.com/v8/v8/blob/d3924c236bfca217385eaf5355a96146f0dcc60d/src/hydrogen-instructions.cc#L2567-2576 < — код увидев 0 выберет целое представление) и почему-то посказки (hint) для HUnknownOSRValue на обратной дуге не сработали.

Кстати, если заменить result = 0, на result = 0.1 баг в representation inference по понятным причинам пропадает.
omega ~/src/temp/perl_vs_node ∳ git diff                                              [master@368da]
diff --git a/integral.js b/integral.js
index 7b11f22..194adb2 100755
--- a/integral.js
+++ b/integral.js
@@ -18,4 +18,5 @@ function integrateJS(x0,xN,y0,yN,iterations){
     console.log("JS time = "+(new Date().getTime() - time));
 }
 
-integrateJS(-4,4,-4,4,1024);
\ No newline at end of file
+integrateJS(-4,4,-4,4,1024);
+integrateJS(-4,4,-4,4,1024);
omega ~/src/temp/perl_vs_node ∳ node integral.js                                      [master@368da]
JS result = 127.99999736028109
JS time = 132
JS result = 127.99999736028109
JS time = 115
omega ~/src/temp/perl_vs_node ∳ node --nouse-osr integral.js                          [master@368da]
JS result = 127.99999736028109
JS time = 122
JS result = 127.99999736028109
JS time = 55


извиняюсь, коряво вставил консольный лог :-/
Проверял PHP и JS, по началу было где-то так, но выключил xdebug и стало

PHP2 time = 601.6321182251
PHP2 result = 127.99999736028

JS time = 204
JS result = 127.99999736028109 
У меня не установлено никакого xdebug. И по моему он по умолчанию не ставится (если честно, я не силен во всех хитростях пхп).
Без xdebug у вас получилась разница в скорости ~3 раза. В моих тестах ~6 раз.
Не исключено что пхп сильно требователен к железу, а т.к. у меня очень слабая виртуалка, то и появилась разница в ваших и моих тестах.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории