Не так давно была статья о выходе 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