Pull to refresh

Тест на скорость ServerSide

Reading time 11 min
Views 2.1K
В последнее время стали очень популярны тесты производительности JavaScript движков, но в основном они касаются Client Side JavaScript. Меня заинтересовал вопрос: как обстоят дела с Server Side? Но тестировать только Google V8 и SpiderMonkey было бы неинтересно. Ясно, что результаты будут схожи с Client Side — движки-то те же. Поэтому нужно было добавить к тестам что-то, что недоступно в браузере, однако достаточно распространено, а также постараться использовать специфичные для серверных задач тесты. Этим недостающим объектом для тестов виделся компилятор JScript из .Net Framework. Однако предварительные результаты тестов стали сюрпризом для меня, и я решил добавить четвертого игрока из той же команды.

.

Прежде всего, следует сказать, что я не ставил целью 100% объективность тестов. Цель была протестировать движки, которые доступны на расстоянии вытянутой руки (5 минут гугления) и в том окружении, которое доступно мне. А это:

Windows XP SP3 на AMD Sempron 2800+ 1,6GHz 1Gb RAM
Windows 7 x64 на Intel Core i5 650 3,2GHz 4Gb RAM

Несмотря на простоту поставленных задач, получение результата растянулось на несколько дней.

Методика тестирования


Была избрана простая методика тестирования. Каждый тест представляет собой некую функцию, которая запускается на выполнение в цикле большое количество раз. Замеряется время выполнения всего цикла. Среднее время выполнения каждой итерации берется в качестве критерия оценки. Я не стал следовать принципам статистической достоверности — запускать тесты несколько раз подряд и вычислять средние отклонения. Этот подход, по моему мнению, требует более глубокого анализа, чем просто запуск тестов несколько раз и вычисление средних отклонений.
Для того чтобы оценить, сколько времени в тесте занимает сам цикл, первым тестом будет пустая функция. Кроме того, в свете последних веяний, которые связаны с технологией удаления мертвого кода, хотелось как-то учесть этот фактор в тестах. Мне не пришло в голову ничего лучше, чем выставить требование к тестируемым функциям возвращать некий результат, который основан на том, что делает конкретная функция. Таким образом, у хитрых методик оптимизаций, я надеюсь, не будет оснований выкидывать «нужный» код. Естественно, хитрая оптимизация может выкинуть код на том основании, что результат работы функции не используется далее. Поэтому к каждой тестируемой функции я добавил дополнительный параметр — функцию для агрегации результатов. Сам агрегированный результат тоже будет результатом теста. Чтобы оценить влияние самой агрегации на результат работы цикла, я запускал его еще раз без агрегации. Это также помогает субъективно оценить наличие оптимизации по исключению мертвого кода.
Для удобства запуска тестов и обработки результатов я написал небольшой фреймворк. Кроме того, для однообразного вывода результатов тестов для каждого движка применялся свой модуль-адаптер. Результаты форматировались в html, выводились в консоль и через перенаправление потоков накапливались в едином файле.
Для запуска всего теста использовался небольшой набор CMD файлов.

Игроки


Первый игрок — это перспективный и набирающий все большую популярность движок Google V8, который работает в Google Chrome, и представлен в тесте в лице не менее перспективного NodeJS. Я был уверен, что результаты тестов не разочаруют меня.
Второй игрок — это достаточно быстрый движок с богатой историей — SpiderMonkey, который работает в FireFox, в тестах он работал в лице JSDB. Тут я тоже не надеялся на какие-то существенные отличия от Client Side тестов.
Третий игрок — компилятор JavaScript от Microsoft. Мне было интересно, какой выигрыш дает использование компилятора, по сравнению с интерпретаторами.
Четвертый игрок — для более полного сравнения хорошо было бы использовать еще интерпретатор от Microsoft.

В случае тестирования интерпретаторов я просто сливал все файлы тестов, фреймворк и адаптеры в один файл командой copy, результирующий файл скармливал интерпретатору. Для NodeJS это nodejs.exe, для JSDB — jsdb.exe, для интерпретатора от Microsoft это Windows Script Host в лице утилиты cscript.exe.
Для компилятора все файлы скармливались jsc.exe из соответствующей версии .Net Framework и запускался результирующий exe файл.

Набор игроков на каждой из платформ отличался.

Windows XP


  1. NodeJS v 0.2.4
  2. JSDB 1.8.0.3
  3. Интерпретатор Microsoft JScript 5.6.8825
  4. Интерпретатор Microsoft JScript 5.7.16599
  5. Интерпретатор Microsoft JScript 5.8.18702
  6. Компилятор JScript из Microsoft .NET v.1.1.4322.2032
  7. Компилятор JScript из Microsoft .NET v.2.0.50727.3053
  8. Компилятор JScript из Microsoft .NET v.4.0.30319.1


Windows 7


  1. NodeJS v 0.2.4
  2. JSDB 1.8.0.3
  3. Интерпретатор Microsoft JScript 5.8.16475
  4. Интерпретатор Microsoft JScript 9.0.16406
  5. Компилятор JScript из Microsoft .NET v.4.0.30319.1


Да. Я не ошибся. Microsoft JScript 9.0.16406 — это тот самый движок, который используется в IE9. Пришлось немного поколдовать, чтоб заставить работать его в Windows Script Host без установки самого IE9. Я даже не знаю, будет ли он доступен в WSH после установки, поскольку библиотека в установочном пакете называется по-другому и рядом с ней лежит старый движок от IE 8, с нормальным именем.
Под Windows 7 мне был еще доступен компилятор от .NET v.2.0, но тест под ним почему-то вываливался с ошибкой, подробности позже.
Я использовал разные версии движков от Microsoft, чтобы увидеть, есть ли прогресс в скорости от версии к версии и какой. Они лежали у меня «под ногами». Почему бы не включить и их в тест?

Тесты



Далее для каждого теста буду приводить тестируемую функцию и результаты для среднего времени цикла по всем движкам. Затем все результаты тестов в конце.

Пустая функция



tests.push({
  name: 'Empty test',
  func: function(){ return 1;},
 reduce: function(r,x){ return r+x;},
 start: 0,
 loops: 1000000
});


* This source code was highlighted with Source Code Highlighter.


тут func — тестируемая функция, reduce — функция-агрегатор, start — стартовое значение агрегатора, loops — количество циклов в тесте.

Результаты Empty test на Windows XP (меньше — лучше)



Результаты Empty test на Windows 7 (меньше — лучше)



Конкатенация строк


Тест типичной операции со строками.

var str1 = 'Hello ',
    str2 = 'world ',
    str3 = 'test ',
    i = 0;
  
  tests.push({
    name: 'String Concat',
    func: function(){
     i++;
     return str1+str2+str3+' '+i;
    },
    reduce: function(r,x){ return r+x.length;},
    start: 0,
    loops: 100000
  });


* This source code was highlighted with Source Code Highlighter.


Результаты String Concat на Windows XP (меньше — лучше)


Результаты String Concat на Windows 7 (меньше — лучше)


Заполнение шаблона данными



На мой взгляд типичная для сервера операция.

   var template = '<html><title>{title}</title><body>{main}</body></html>';
  
  function ApplyTemplate(template, view){
    return template.replace(/{(\w+)}/g,function(p,n){
      if(n in view) return view[n];
      else return '_ERROR_NO_VALUE_'+n;            
    });  
  }  
  
  var views = [
         {
         title:'Page One',
           main: 'Lorem ipsum dolor sit amet, mauris libero velit, '+
'vitae pellentesque aliquam, cursus magnis velit, non '+
'viverra sed nibh ac fringilla vel, accumsan quis '+
'elementum fermentum ullamcorper. '+
/* ....тут много текста */
'lectus libero at etiam morbi, et orci eros ut sit et.'
         },
         {
          title: 'Page two',
          main: 'Short page'
         }  
         ];
   
  var counter = 0;
  tests.push({
    name: 'Apply template',
    func: function(){
     counter++;
     return ApplyTemplate(template, views[counter%2]);
    },
    reduce: function(r,x){ return r+x.length;},
    start: 0,
    loops: 10000
  });


* This source code was highlighted with Source Code Highlighter.


Результаты Apply template на Windows XP (меньше — лучше)


Результаты Apply template на Windows 7 (меньше — лучше)


Следующие тесты я взял из Google V8 Benchmark Suite version 6.

К сожалению, оказалось, что в этих тестах полно ошибок. На ошибки мне указывал компилятор JScript. Часть ошибок была связана с неполной совместимостью JScript и JavaScript. Часть действительно была ошибками. Я взял 3 теста, которые создавали меньше всего проблем. Немного адаптировал под свой фреймворк, исправил ошибки. Можно было бы взять тесты из Sunspider, но, к сожалению, я не нашел их в виде удобных js исходников. Версия, представленная на сайте с тестом, требует обработки парсером, поскольку встроена в html.
Не буду приводить тут исходники этих тестов — только результаты.

V8 Benchmark Suite Richards


Как сказано на сайте, симулирует работу ядра ОС.

Результаты V8 BS — Richards на Windows XP (меньше — лучше)


Результаты V8 BS — Richards на Windows 7 (меньше — лучше)



V8 Benchmark Suite Encrypt


Кодирование данных.

Результаты V8 BS — Encrypt на Windows XP (меньше — лучше)


Результаты V8 BS — Encrypt на Windows 7 (меньше — лучше)


V8 Benchmark Suite Decrypt


Декодирование данных.

Результаты V8 BS — Decrypt на Windows XP (меньше — лучше)


Результаты V8 BS — Decrypt на Windows 7 (меньше — лучше)


V8 Benchmark Suite RegExp


Регулярные выражения, извлеченные из 50 популярных сайтов в Интернет.

Результаты V8 BS — RegExp на Windows XP (меньше — лучше)


Результаты V8 BS — RegExp на Windows 7 (меньше — лучше)


Полная таблица результатов на Windows XP.
NodeJS v 0.2.4
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 21 0.000021 44 0.000044 1000000
String Concat 100000 44 0.00044 47 0.00047 2288895
Apply template 10000 32 0.0032 28 0.0028 590000
V8 BS — Richards 1000 4879 4.879 4702 4.702 0
V8 BS — Encrypt 100 345 3.45 356 3.56 0
V8 BS — Decrypt 100 7496 74.96 7514 75.14 0
V8 BS — Regexp 100 11620 116.2 11913 119.13 0

Microsoft .NET v.1.1.4322.2032
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 1891 0.001891 4094 0.004094 1000000
String Concat 100000 344 0.00344 859 0.00859 2288895
Apply template 10000 532 0.0532 609 0.0609 590000
V8 BS — Richards 25 9266 370.64 9390 375.6 0
V8 BS — Encrypt 10 2875 287.5 3062 306.2 0
V8 BS — Decrypt 5 33657 6731.4 32078 6415.6 0
V8 BS — Regexp 10 32296 3229.6 33219 3321.9 0

Microsoft .NET v.2.0.50727.3053
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 3328 0.003328 8578 0.008578 1000000
String Concat 100000 515 0.00515 1297 0.01297 2288895
Apply template 10000 547 0.0547 657 0.0657 590000
V8 BS — Richards 25 9703 388.12 9859 394.36 0
V8 BS — Encrypt 10 2531 253.1 2875 287.5 0
V8 BS — Decrypt 5 25344 5068.8 25047 5009.4 0
V8 BS — Regexp 10 29032 2903.2 29234 2923.4 0

Microsoft .NET v.4.0.30319.1
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 2188 0.002188 5282 0.005282 1000000
String Concat 100000 437 0.00437 1125 0.01125 2288895
Apply template 10000 500 0.05 610 0.061 590000
V8 BS — Richards 25 9812 392.48 10031 401.24 0
V8 BS — Encrypt 10 2719 271.9 3469 346.9 0
V8 BS — Decrypt 5 28140 5628 28297 5659.4 0
V8 BS — Regexp 10 34437 3443.7 33016 3301.6 0

JSDB 1.8.0.3
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 343 0.000343 719 0.000719 1000000
String Concat 100000 703 0.00703 547 0.00547 2288895
Apply template 10000 383 0.0383 233 0.0233 590000
V8 BS — Richards 1000 48319 48.319 49948 49.948 0
V8 BS — Encrypt 10 905 90.5 925 92.5 0
V8 BS — Decrypt 10 16571 1657.1 16475 1647.5 0
V8 BS — Regexp 10 15968 1596.8 13636 1363.6 0

Microsoft JScript 5.6.8825
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 2578 0.002578 5812 0.005812 1000000
String Concat 100000 4531 0.04531 4844 0.04844 2288895
Apply template 10000 1344 0.1344 1375 0.1375 410000
V8 BS — Richards 100 18484 184.84 18422 184.22 0
V8 BS — Encrypt 10 1813 181.3 1797 179.7 0
V8 BS — Decrypt 10 32500 3250 32187 3218.7 0
V8 BS — Regexp 10 37938 3793.8 37547 3754.7 0

Microsoft JScript 5.7.16599
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 4235 0.004235 6562 0.006562 1000000
String Concat 100000 1625 0.01625 1969 0.01969 2288895
Apply template 10000 562 0.0562 594 0.0594 410000
V8 BS — Richards 100 22328 223.28 22625 226.25 0
V8 BS — Encrypt 10 1797 179.7 1813 181.3 0
V8 BS — Decrypt 10 33953 3395.3 32219 3221.9 0
V8 BS — Regexp 10 27813 2781.3 29609 2960.9 0

Microsoft JScript 5.8.18702
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 1171 0.001171 1532 0.001532 1000000
String Concat 100000 844 0.00844 1235 0.01235 2288895
Apply template 10000 359 0.0359 359 0.0359 410000
V8 BS — Richards 100 11906 119.06 12094 120.94 0
V8 BS — Encrypt 10 1610 161 1578 157.8 0
V8 BS — Decrypt 10 26875 2687.5 27281 2728.1 0
V8 BS — Regexp 10 22781 2278.1 22391 2239.1 0



Полная таблица результатов на Windows 7.
NodeJS v 0.2.4
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 12 0.000012 14 0.000014 1000000
String Concat 100000 17 0.00017 13 0.00013 2288895
Apply template 10000 9 0.0009 11 0.0011 590000
V8 BS — Richards 1000 1640 1.64 1749 1.749 0
V8 BS — Encrypt 100 120 1.2 128 1.28 0
V8 BS — Decrypt 100 2570 25.7 2573 25.73 0
V8 BS — Regexp 100 3996 39.96 3996 39.96 0

JSDB 1.8.0.3
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 89 0.000089 221 0.000221 1000000
String Concat 100000 177 0.00177 167 0.00167 2288895
Apply template 10000 33 0.0033 33 0.0033 590000
V8 BS — Richards 1000 17738 17.738 17780 17.78 0
V8 BS — Encrypt 10 272 27.2 276 27.6 0
V8 BS — Decrypt 10 5095 509.5 5103 510.3 0
V8 BS — Regexp 10 3454 345.4 3499 349.9 0

Microsoft .NET v.4.0.30319.1
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 574 0.000574 1344 0.001344 1000000
String Concat 100000 149 0.00149 334 0.00334 2288895
Apply template 10000 140 0.014 281 0.0281 590000
V8 BS — Richards 25 2268 90.72 2576 103.04 0
V8 BS — Encrypt 10 471 47.1 2813 281.3 0
V8 BS — Decrypt 5 4908 981.6 5018 1003.6 0
V8 BS — Regexp 10 8762 876.2 8805 880.5 0

Microsoft .NET v.4.0.30319.1
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 516 0.000516 1293 0.001293 1000000
String Concat 100000 118 0.00118 273 0.00273 2288895
Apply template 10000 136 0.0136 301 0.0301 590000
V8 BS — Richards 25 2188 87.52 2477 99.08 0
V8 BS — Encrypt 10 470 47 3000 300 0
V8 BS — Decrypt 5 4831 966.2 5049 1009.8 0
V8 BS — Regexp 10 8704 870.4 8826 882.6 0

Microsoft JScript 5.8.16475
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 244 0.000244 502 0.000502 1000000
String Concat 100000 168 0.00168 224 0.00224 2288895
Apply template 10000 90 0.009 95 0.0095 410000
V8 BS — Richards 100 4391 43.91 4400 44 0
V8 BS — Encrypt 10 611 61.1 619 61.9 0
V8 BS — Decrypt 10 10502 1050.2 10512 1051.2 0
V8 BS — Regexp 10 4768 476.8 4772 477.2 0

Microsoft JScript 9.0.16406
Name Loops Time Avg Time wr Avg wr Res
Empty test 1000000 41 0.000041 82 0.000082 1000000
String Concat 100000 38 0.00038 39 0.00039 2288895
Apply template 10000 18 0.0018 17 0.0017 590000
V8 BS — Richards 100 166 1.66 166 1.66 0
V8 BS — Encrypt 10 15 1.5 37 3.7 0
V8 BS — Decrypt 10 253 25.3 246 24.6 0
V8 BS — Regexp 10 3175 317.5 3188 318.8 0


Выводы


Компилятор .NET (точнее результаты его работы) оказался самым медленным. Либо я что-то не так делаю, либо он просто ни на что не годен. Интерпретатор от Microsoft версий менее 9-й провалил тест на применение шаблона — результат отличается от остальных. Более внимательное изучение показало, что в нем отличаются назначения параметров функции обратного вызова для replace.
Под Windows 7 не удалось протестировать версию компилятора от .NET Framework 2.0, а версия 4.0 вела себя не менее странно. Тест работал 20 минут. Версия 2.0 вылетала с ошибкой. Для обеих версий тест съедал до 6 Гб памяти. Почему сами тесты показали не такие катастрофические времена? Более внимательное изучение проблемы выявило странную особенность компилятора под Windows 7 x64: проблемы создавал тест V8 BS — Regexp. Причем откомпилированная программа начинала пожирать память еще до начала выполнения моего кода. Похоже .Net как-то инициализирует регулярные выражения, которых в этом тесте в избытке, еще до выполнения кода. Причем проблемы наблюдаются, если регулярные выражения находятся в функции, которая создает замыкание.
Мой окончательный вывод такой: компилятор JScript из .NET Framework ни на что не годен, кроме тестирования кода на ошибки при компиляции. Мне так же стало интересно протестировать, чем будет отличаться С# на подобных задачах. Впрочем, «скоростной» провал JScript .NET начинает проясняться, если посмотреть на трассировку стека при выводе сообщений об ошибках. Такого количества оберток на простых вызовах я еще не видел.
Однако, новый интерпретатор от Microsoft JScript 9.0 вполне конкурентоспособен, его легко расширить своими классами через ActiveScripting. Интересным видится выбор версии. Почему после версии 5.8, которая использовалась в IE8 вдруг вышла версия 9.0? Впрочем, после официального релиза все может поменяться.
Лидер по сокрости Google V8 в NodeJS, но JScript 9.0 наступает на пятки. Мне кажется, что окончательный выбор можно смело делать не только по критерию скорости, а по сумме факторов. В том числе по удобству расширения, отладки, интеграции с платформой.

И в конце — ничего не значащая, чисто иллюстративная диаграмма. Я просто взял отношение времени NodeJS ко времени каждого теста и усреднил по тестам.

Рейтинг движков (больше — лучше)


Ссылки:

NodeJS
JSDB
Google V8 Benchmark Suite version 6
Microsoft Windows Script Host
Microsoft JScript
Tags:
Hubs:
+53
Comments 30
Comments Comments 30

Articles