- PVSM.RU - https://www.pvsm.ru -
Не так давно, прочитав статью 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, регулярные выражения с текущим состоянием и добавленными пользователем свойствами. Мой модуль с этими ситуациями справляется так:
Как обстоят дела у конкурентов, можно оценить здесь [14].
Мне кажется, получилось очень даже достойно.
Вполне закономерно было бы предположить, что из-за поддержки высокого качества клонирования должна была упасть скорость. И скорость действительно упала, но не настолько, чтобы node-v8-clone потерял первенство в большинстве ситуаций.
Например, вот результаты поверхностного клонирования объекта из 100 элементов {'_0': '_0', ..., '_99': '_99'}
(в операциях в секунду):
Глубокое клонирование 500 вложенных массивов, расположенных в 4 уровня вложенности, которые содержат 900 строк (тут также сравнивается оптимизированная версия [15] клонирования из node-v8-clone, которая не проходит еще один тест, но существенно ускоряет работу глубоким клонированием массивов):
А вот с небольшими массивами дела обстоят несколько хуже. Тут сказывается дороговизна обращения к c++-модулю, так что у дешевых алгоритмов типа появляется преимущество.
Все результаты бенчмарков. [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/
Нажмите здесь для печати.