- PVSM.RU - https://www.pvsm.ru -

Клонирование объектов в Node.js: Быстрее, глубже, нежнее!

Не так давно, прочитав статью idoroshenko [1] «Почему eval — это не всегда плохо» [2], я задумался, можно ли использовать подход с генерацией тела функции для клонирования объектов. Даже написал небольшую библиотеку для этого. Бенчмарки давали невероятные результаты, но применимость этого подхода ограничивалась лишь множественным клонированием одинаковых объектов.

Поэтому и у меня возник вопрос: неужели в v8 нет другой возможности избежать расходов, связанных со множественным пересозданием скрытых классов? Ведь это составляет основные траты ресурсов, когда мы клонируем объекты. Как оказалось, такая возможность действительно есть: в самом v8 у объектов существует метод v8::Object::Clone [3]. Этот метод клонирует объекты в широком смысле этого слова, то есть собственно объекты, а также массивы, даты, регулярные выражения, функции и т.д., при этом сохраняя все их свойства, в том числе нестандартные (например, именованные свойства массивов) и даже скрытые.

Была только одна маленькая проблема. Этот метод использовался только в недрах node.js, и не был открыт наружу, для javascript'а.

Недолго думая, я полез в документацию node.js по созданию расширений на c++ [4] и написал пробную версию [5] модуля, который просто раскрывает эту функцию.

Получив ускорение для разных объектов примерно в 10-100 раз, я понял, что у этой техники есть большой потенциал, и начал его воплощать в жизнь в модуле node-v8-clone [6] (npm [7]), стараясь этот потенциал не растерять по пути, применяя смесь TDD и benchmark driven development. Это позволило следить за скоростью при разработке и исправлении проблем, а также следить за регрессиями при оптимизациях. Заодно, раз бенчмарки и тесты были готовы, я решил сравнить свой модуль с другими:

Качество клонирования

Одной из целей было добиться максимального качества клонирования. Пришлось поднапрячь фантазию, чтобы придумать достаточное количество неприятных для процесса клонирования ситуаций. В число таких входят, например, функции, замыкания, arguments, регулярные выражения с текущим состоянием и добавленными пользователем свойствами. Мой модуль с этими ситуациями справляется так:

Клонирование объектов в Node.js: Быстрее, глубже, нежнее!
Как обстоят дела у конкурентов, можно оценить здесь [14].

Мне кажется, получилось очень даже достойно.

Скорость

Вполне закономерно было бы предположить, что из-за поддержки высокого качества клонирования должна была упасть скорость. И скорость действительно упала, но не настолько, чтобы node-v8-clone потерял первенство в большинстве ситуаций.
Например, вот результаты поверхностного клонирования объекта из 100 элементов {'_0': '_0', ..., '_99': '_99'} (в операциях в секунду):
Клонирование объектов в Node.js: Быстрее, глубже, нежнее!
Глубокое клонирование 500 вложенных массивов, расположенных в 4 уровня вложенности, которые содержат 900 строк (тут также сравнивается оптимизированная версия [15] клонирования из node-v8-clone, которая не проходит еще один тест, но существенно ускоряет работу глубоким клонированием массивов):
Клонирование объектов в Node.js: Быстрее, глубже, нежнее!
А вот с небольшими массивами дела обстоят несколько хуже. Тут сказывается дороговизна обращения к c++-модулю, так что у дешевых алгоритмов типа появляется преимущество.
Клонирование объектов в Node.js: Быстрее, глубже, нежнее!

Все результаты бенчмарков. [16]

Что дальше

Собираюсь починить клонирование node.js-овских буферов. Сейчас они клонируются (a !== b), но указывают на одну и ту же область памяти и содержимое у них оказывается все еще связанным.
Хотелось бы починить клонирование arguments. Когда у функции есть аргументы, объект arguments оказывается связанным с контекстом функции, и при клонировании они тоже оказываются связанными между собой.
Хотелось бы придумать еще больше каверзных входящих данных, например файловые дескрипторы, модули, таймеры… Я не рассчитываю, что у меня получится их по-настоящему клонировать, но как минимум хотелось бы понимать, как будет вести себя с ними этот модуль.

Буду рад любым отзывам, предложеним, багам и патчам. Ну и fork me on GitHub [6] :)

Автор: wickedweasel

Источник [17]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/node-js/23724

Ссылки в тексте:

[1] idoroshenko: http://habrahabr.ru/users/idoroshenko/

[2] «Почему eval — это не всегда плохо»: http://habrahabr.ru/post/158403/

[3] v8::Object::Clone: http://bespin.cz/~ondras/html/classv8_1_1Object.html#a45a67c1b0408922403048b59a5bc22b2

[4] созданию расширений на c++: http://nodejs.org/api/addons.html

[5] пробную версию: https://github.com/AlexeyKupershtokh/node-v8-clone/blob/cc0d5dfad4ab2918654cc785f2f96e0421508834/lib/clone.cc

[6] node-v8-clone: https://github.com/AlexeyKupershtokh/node-v8-clone

[7] npm: https://npmjs.org/package/node-v8-clone

[8] lodash: https://npmjs.org/package/lodash

[9] недавно писал: http://habrahabr.ru/company/alawar/blog/157673/

[10] underscore: https://npmjs.org/package/underscore

[11] owl-deepcopy: https://npmjs.org/package/owl-deepcopy

[12] clone: https://npmjs.org/package/clone

[13] cloneextend: https://npmjs.org/package/cloneextend

[14] здесь: https://github.com/AlexeyKupershtokh/node-v8-clone/wiki/Test-results

[15] оптимизированная версия: https://github.com/AlexeyKupershtokh/node-v8-clone/blob/master/examples/opt.js

[16] Все результаты бенчмарков.: https://github.com/AlexeyKupershtokh/node-v8-clone/wiki/Benchmark-graphs

[17] Источник: http://habrahabr.ru/post/147124/