Всегда ли библиотеки на С быстрее чем JS

в 15:28, , рубрики: javascript, node.js, performance, высокая производительность, метки: , ,

Не так давно была статья о выходе js-yaml 2.0.0, который был полностью написан на JS и при этом работал весьма быстро. И только недавно я узнал, насколько же все хорошо на самом деле :). А началось все с того, что знакомый пожаловался на руби, который долго парсил 7-мегабайтный yaml-файл. Это было довольно странно, потому что руби использует биндинги к libyaml. Мы написали несколько примитивных тестов для руби с питоном, и получили такие результаты, что я заподозрил ошибку.

Тогда создал тему на LOR, там нашли пару косяков и добавили примеров с других языков. Картину мира это улучшило, но не сильно. JS подозрительно быстр. Результат ниже, а в конце пара замечаний.

Сразу оговорюсь, что текущие примеры не являются грамотными тестами. Там не учитывается много вещей, которые влияют на точность. Зато просто, дешево, сердито, и хватает для качественной оценки. Когда исходные данные отличаются на порядок, то ошибка в 2 раза мало кого волнует — разницу вы все равно увидите, и поймете что она большая.

Тестовый сид лежит на гитхабе gist.github.com/anonymous/5028302

Поехали

кросафчег js-yaml (чистый JS):

time node -e "var yaml=require('js-yaml'), text=require('fs').readFileSync('data.yml', 'utf-8'); yaml.load(text);"

real	0m0.984s
user	0m0.940s
sys	0m0.040s

node.js & libyaml:

time node -e "require('libyaml').readFileSync('data.yml');"

real	0m3.424s
user	0m3.300s
sys	0m0.136s

python & libyaml:

time python -c "import yaml; from yaml import CLoader as Loader; yaml.load(open('data.yml'),Loader=Loader)"

real	0m5.530s
user	0m5.268s
sys	0m0.212s

perl & libyaml:

time perl -MYAML::XS -e 'YAML::XS::LoadFile "data.yml"'

real	0m0.484s
user	0m0.456s
sys	0m0.024s

php & libyaml:

time php -r 'yaml_parse_file("data.yml");'

0,49s user 
0,04s system 
99% cpu 
0,537 total

ruby 2.0 & libyaml:

time ruby -r psych -e "Psych.load_file 'data.yml'"

6.34s user
0.09s system
99% cpu
6.494 total

ruby 2.0 & syck:

time ruby -r syck -e "Syck.load_file('data.yml')"                          

0.87s user
0.02s system
96% cpu
0.920 total

JSON node.js (просто для сравнения)

time node -e "var text=require('fs').readFileSync('data.json', 'utf-8'); JSON.parse(text);"

real	0m0.265s
user	0m0.152s
sys	0m0.040s

Оригинал живет и пополняется на github.

Итого

А имеем мы в итоге любопытный результат:

  • кривые биндинги могут запороть весь профит от бинарного кода
  • js-yaml даже при том, что не до конца оптимизирован, всего лишь в 2-3 раза уступает самым удачным биндингам (perl/php)

И еще — даже если вы во внешней библиотеке очень быстро все посчитаете, то импорт данных в runtime не бесплатный. Особенно для динамических языков. Может так получится, что вынос кода в компилированные библиотеки потеряет смысл.

Возвращаясь к JS, в среднем по больнице получается, что при правильном подходе потребность в бинарном коде невелика. А чтобы постичь глубины мудрости, нужно посмотреть выступление Вячеслава Егорова youtu.be/tCG0aPNvkTs об оптимизациях в v8.

PS. По моим прикидкам, js-yaml можно ускорить еще раза в 2 или больше. Если кто-то хочет набить руку на оптимизациях JS — обращайтесь :)

Автор: Vitaly

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js