Вчера, профилируя одно из наших приложений NodeJS, я наткнулся на интересный фрагмент кода, выполнение которого, казалось, занимало слишком много времени: что интересно, все, казалось, указывало на функцию pick lodash.

Для тех из вас, кто не знает, что делает эта функция, она в основном создает новый объект из существующего, выбирая только определенный пользователем список свойств для «перемещения».

Кодом проще, чем словами:

let a = {b: 1, c: 2}
_.pick(a, ['b']) // {b: 1}

Это своего рода элегантное решение по сравнению с такими операциями вручную:

С точки зрения стиля он практически не изменился, хотя, на мой взгляд, выглядит немного элегантнее и позволяет набирать меньше кода.

Так что насчет влияния на производительность? Я написал небольшой скрипт, чтобы протестировать его на длинном списке «пользовательских» объектов:

Итак, мы создаем список из тысячи объектов с горсткой свойств, просматриваем этот список и сопоставляем их, вручную выбирая пару свойств. Результат:

/tmp ᐅ node beware-lodash.js
vanilla: 1.064ms
lodash: 14.271ms

Что на самом деле [ругательства] здесь происходит?

Что ж, Lodash необходимо перебрать все ключи в объекте, чтобы выбрать конкретные свойства, и, кроме того, необходимо вызвать целую кучу функций:

  • pick
  • basePick
  • basePickBy
  • hasIn
  • hasPath
  • castPath
  • toKey

… И я честно потерял счет к тому времени - интересно, что это делает со стеком :)

Итак, хотя я не могу точно определить узкое место, я думаю, что существует множество различных факторов, которые замедляют его реализацию по сравнению с использованием более утомительного, ванильного JS: Big O сам по себе менее эффективен, плюс факт то, что ему нужно вызывать несколько разных функций и делать некоторые проверки работоспособности, определенно играет роль в замедлении.

В нашем случае мы использовали lodash в цикле, который, в худшем случае, включал ~ 100 элементов:

# Use list.fill(100)
/tmp ᐅ node beware-lodash.js
vanilla: 0.357ms
lodash: 5.250ms

Угадайте, сколько времени мы сэкономили в нашем приложении? Ровно 5 мс.

Многим это может показаться не слишком большим, но, учитывая, что время отклика данной услуги составляло около 44 мс, мы фактически сократили его на 10%.

Означает ли это, что Lodash - отстой?

Черт возьми, нет! Как бы вы это сделали, не написав вручную кучу шаблонов?

Как всегда, есть вариант использования для всего, поэтому вам просто нужно понять, подходит ли функция, API или библиотека, которые вы используете, для вашего сценария: для нас, поскольку мы всегда знали, какие свойства нужно выбирать заранее, используя _.pick и вся мощь, которую он предлагает, не имела большого смысла.

Слишком часто мы полагаемся на библиотеки и абстракции, думая, что никогда не собираемся «платить» за это: учитывая, что эти абстракции часто необходимы, чтобы мы не изобретали колесо заново и не полагались на чью-то великую работу, мы по-прежнему необходимо оценить, какие последствия принесет использование фреймворка или библиотеки.

Ваше здоровье!

Первоначально опубликовано на odino.org ( 24 марта 2017 ).