На примере простой задачи клонирования ключей объекта посмотрим, есть ли реальные альтернативы по производительности столь презираемой JavaScript-разработчиками функции eval(). Подобная задача возникает, если оригинальное значение ключа надо оставить у объекта, а как-то обработанное - положить рядом в новый соответствующий ключ. То есть, для начала, из {"a" : 1, "b" : 2} надо получить {"a" : 1, "a-copy" : 1, "b" : 2, "b-copy" : 2}. Пару лет назад я уже рассказывал, почему максимальная производительность подобных операций на JavaScript важна для нашего сервиса потокового анализа логов PostgreSQL, как можно поускорять парсинг с помощью WebAssembly, и вот сегодня - продолжение. Сначала договоримся, что объекты у нас не совсем произвольные, а имеют вполне конкретные интересные нам ключи 'a'..'h' и 'mask' - битовую маску, сигнализирующую их присутствие в объекте. То есть имеют вид вроде: Чтобы все наши тесты проходили на едином наборе объектов, сгенерируем его и сохраним в файл: Чтобы оттолкнуться хоть от какого-то значения производительности, напишем самый "примитивный" вариант кода, который просто перебирает все ключи объекта и создает новые для интересующих нас: Этот код для наших 64K объектов показывает время порядка 131ms. В коде выше, как минимум, две нехорошие с точки зрения производительности вещи: includes проверяет наличие ключа в массиве за линейное время, в худшем случае, полностью перебирая весь массив; `${key}-copy` создает новую строку каждый раз. Исправим это, заменив includes на in-проверку наличия ключа в объекте, а формирование строки - ее извлечением из этого же объекта: Такой код уже выполняется на 20% быстрее! Но мы все равно тут обращаемся в repl-объект дважды. Исправим и это: Мелочь - а приятно! Но во всех предыдущих вариантах мы никак не использовали тот факт, что нам заранее известно, какие ключи есть в объекте, и перебирали и фильтровали их все. А давайте будем перебирать только нужные, примерно как использовали это при генерации? Почти в 2.5 раза быстрее! Но зачем перебирать все биты маски, если мы точно знаем, что оставшиеся старшие - нулевые? Можно и не перебирать: Выиграли копейку, но она - рубль бережет! Теперь обратим внимание, что сам перечень ключей каждого объекта мы все равно "перебираем" - а давайте не будем? Для этого всего-то надо нужный набор ключей и их замен сгенерировать для каждой маски заранее (их всего-то 2 ^ nKeys): Однако, еще на 15% эффективнее! Очевидно, что раз уж мы заранее знаем по каждой маске, какие ключи должны быть скопированы, то почему бы не превратить это в готовую функцию? Ой... внезапно откатились к предыдущему результату: Давайте резюмируем, что у нас получилось на выборке 64K объектов - лучшим оказался for-of по предрассчитанному набору ключей для каждой маски: Может, у нас движок V8 не успел "прогреть" каждую из функций? Попробуем тест для миллиона объектов: Нет, это не случайность, и банальный перебор объектов все равно быстрее. Но уже не на 16%, а всего на 4%! Ну-ка, ну-ка... Промеряем для разного количества объектов: А eval-то все-таки оказывается существенно быстрее, если их достаточно много! Очевидно, что данные для 1M на данном графике представляют некоторую аномалию, и вместо +4% должно было получиться -10%, но отложим пока этот вопрос. И, если следовать графику, то в нашем случае для комбинаций из 8 ключей, начиная примерно с 200K объектов, eval - лучший выбор!  Архитектура ИС: PostgreSQL, Node.js и highload