Вступление
Появилась необходимость обмениваться сообщениями между сервером и клиентом в бинарном виде, но в формате JSON в конечном итоге. Начал я гуглить, какие существуют библиотеки упаковки в бинарный вид. Пересмотрел немало: MesssagePack, Bson, protobuf, capnproto.org и другие. Но эти все библиотеки позволяют паковать и распаковывать готовые бинарные пакеты. Не очень копался, возможно ли делать парсер входящего трафика по кускам. Но суть не в этом. С такой задачей никогда не сталкивался и решил поиграться с нодой и сделать свой. Куда же без костылей и велосипедов? И вот с какими особенностями Node.js я столкнулся…
Написал я пакер и запустил…
var start = Date.now(); for (i=0; i < 1000000; i++) { packer.pack({abc: 123, cde: 5}); } console.log(Date.now() - start);
Выдал ~4300. Удивился… Почему так долго? В то время, как код:
var start = Date.now(); for (i=0; i < 1000000; i++) { JSON.stringify({abc: 123, cde: 5}); } console.log(Date.now() - start);
Выдал ~350. Не понял. Начал копать свой код и искать, где же много ресурсов используется. И нашел.
Запустим этот код:
function find(val){ function index (value) { return [1,2,3].indexOf(value); } return index(val); } var start = Date.now(); for (i=0; i < 1000000; i++) { find(2); } console.log(Date.now() - start);
Выдает 1908. Вы скажете: да это не много на 1000000 повторений. А если я скажу, что много? Выполним такой код:
function index (value) { return [1,2,3].indexOf(value); } function find(val){ return index(val); } var start = Date.now(); for (i=0; i < 1000000; i++) { find(2); } console.log(Date.now() - start);
Выдает 16. Мои коллеги тоже возмутились, но и заметили, что функция же создается динамически и сразу уничтожается, ты ее вынес и нет такой нагрузки. Из эксперимента вывод: динамические фунции не кешируюся в бинарном виде. Я согласился и возразил: да, но нет ни переменных в SCOPE ничего используемого внутри нее. Похоже, движок гугла всегда копирует SCOPE.
Ок. Провел оптимизацию этой фунциональности и запустил… и все равно. Выдал ~3000. Опять удивился. И снова полез копать… и обнаружил уже другой прикол.
Запустим этот код:
function test (object) { var a = 1, b = [], c = 0 return { abc: function (val) { } } } var start = Date.now(); for (i=0; i < 1000000; i++) { var a = test(); a.abc(); } console.log(Date.now() - start);
Выдал 34. Теперь, допустим, нам надо внутри abc создать Array:
function test (object) { var a = 1, b = [], c = 0 return { abc: function () { var arr1 = []; } } } var start = Date.now(); for (i=0; i < 1000000; i++) { var a = test(); a.abc(); } console.log(Date.now() - start);
Выдал 1826. Смеркалось… А если нам надо 3 массива?
function test (object) { var a = 1, b = [], c = 0 return { abc: function () { var arr1 = [], arr2 = [], arr3 = []; } } } var start = Date.now(); for (i=0; i < 1000000; i++) { var a = test(); a.abc(); } console.log(Date.now() - start);
Выдал 5302! Вот это приколы. Казалось, SCOPE мы не используем, а создание пустого массива должно занимать вообще копейки. Не тут то было.
Думаю… А заменю-ка я на объекты. Результат получше, но не намного. Выдал 1071.
А теперь фокус. Многие скажут: ты же опять выносишь функцию. Да. Но фокус в другом.
function abc () { var arr1 = [], arr2 = [], arr3 = []; } function test (object) { var a = 1, b = [], c = 0 return { abc: abc } } var start = Date.now(); for (i=0; i < 1000000; i++) { var a = test(); a.abc(); } console.log(Date.now() - start);
Многие заметят и скажут: будет такое же время. А не тут то было. Выдал 25. Хотя массивы создавались столько же раз. Делаем вывод: создание массивов в динамической функции тратит много ресурсов. Вопрос: почему?
Теперь вернемся к первой проблеме. Но с другой стороны. Вынесем Array:
var indexes = [1,2,3]; function find(val){ function index (value) { return indexes.indexOf(value); } return index(val); } var start = Date.now(); for (i=0; i < 1000000; i++) { find(2); } console.log(Date.now() - start);
И я был прав. Выдал 58. С выносом всей фунции выдавал 16. Т.е. создание функции не особо ресурсоемкий процесс. Также опровергаем прошлый вывод:
бинарный код функций все же кешируется в памяти. А создание объектов в динамической функции занимает много времени.
Я раньше предполагал по-другому: все static/expression объекты, создаваемые временно, компилируются сразу как код функции. А, оказывается, нет. Делаем вывод:
движок гугла при каждом запуске создает новые объекты и заполняет необходимыми значениями, а потом уже вычисляет выражение, что не хорошо.
А с какими тонкостями сталкивались вы? Комментарии приветствуются.
ссылка на оригинал статьи https://habrahabr.ru/post/277823/
Добавить комментарий