Разница в производительности между предложениями Lodash «get» и «if else»

#javascript #json #typescript #lodash #benchmarking

#javascript #json #typescript #Lodash #сравнительный анализ

Вопрос:

Допустим, у вас есть объект typescript, где может быть любой элемент undefined . Если вы хотите получить доступ к сильно вложенному компоненту, вам придется провести множество сравнений с undefined .

Я хотел сравнить два способа сделать это с точки зрения производительности: регулярные if-else сравнения и функцию Lodash get .

Я нашел этот прекрасный инструмент под названием jsben, с помощью которого можно тестировать различные фрагменты js-кода. Однако я не могу правильно интерпретировать результаты.

В этом тесте Lodash get кажется немного быстрее. Однако, если я определяю свою переменную в Setup block (в отличие от Boilerplate code ), if-else код выполняется быстрее с большим отрывом.

Каков правильный способ сравнительного анализа всего этого? Как я должен интерпретировать результаты? Это get настолько медленнее, что вы можете привести аргумент в пользу if-else предложений, несмотря на очень плохую читаемость?

Комментарии:

1. Используйте тот, который более удобочитаем. Я запустил оба ваших теста и не получил существенной разницы между двумя…

Ответ №1:

Я думаю, вы задаете неправильный вопрос.

Прежде всего, если вы собираетесь выполнять микрооптимизацию производительности (в отличие, скажем, от алгоритмической оптимизации), вы действительно должны знать, является ли рассматриваемый код узким местом в вашей системе. Устраняйте наихудшие узкие места до тех пор, пока ваша производительность не станет нормальной, а затем перестаньте слишком беспокоиться об этом. Я был бы весьма удивлен, если бы разница между ними когда-либо составляла нечто большее, чем ошибка округления в серьезном приложении. Но я был удивлен раньше; следовательно, необходимо протестировать.

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

Учитывая это, кажется, что у families amp;amp; families.Trump amp;amp; families.Trump.members amp;amp; ... метода есть очень небольшое преимущество в производительности. (Примечание: здесь не видно ни if s, ни else s!)

Но стоит ли оно того? Я бы сказал, что нет. Код намного, намного уродливее. Я бы не стал добавлять библиотеку, такую как Lodash (или мою любимую, Ramda), только для использования такой простой функции, как эта, но если бы я уже использовал Lodash, я бы без колебаний использовал здесь более простой код. И я мог бы импортировать один из Lodash или Ramda или просто написать свой собственный в противном случае, поскольку это довольно простой код.

То, что машинный код будет быстрее, чем более универсальный библиотечный код, не должно удивлять. Это происходит не всегда, так как иногда библиотекам приходится использовать ярлыки, недоступные собственному движку, но, скорее всего, это норма. Причина использования этих библиотек редко связана с производительностью, но связана с написанием более выразительного кода. Здесь выигрывает версия Lodash, без раздумий.

Комментарии:

1. «иногда библиотекам приходится использовать ярлыки, недоступные собственному движку» Я бы скорее сказал, что авторы библиотек иногда действительно знают, что они делают…

2. @JonasWilms: Я надеюсь на это (как один из авторов этой библиотеки сам.) . Дело в том, что спецификация для Array.prototype.map довольно сложная. Но MyLib.map не обязательно даже пытаться делать все, что он делает. Он также может сделать больше. Иногда это приводит к значительным различиям в производительности.

Ответ №2:

Каков правильный способ сравнительного анализа всего этого?

Тестируйте только фактический код, который вы сравниваете, перемещайте как можно больше за пределы тестируемого блока. Запустите каждую из двух частей несколько (сотен) тысяч раз, чтобы усреднить влияние других частей.

Как я должен интерпретировать результаты?

1) проверьте, действительны ли они:

Соответствуют ли результаты вашим ожиданиям? Если нет, может ли быть причина для этого? Копирует ли тестовый набор ваш фактический пользовательский набор?

2) проверьте, является ли результат релевантным:

Как время, которое требуется для этого, сравнивается с фактическим временем в вашем usecase? Если загрузка вашего кода занимает 200 мс, а оба теста выполняются менее чем за ~ 1 мс, ваш результат не имеет значения. Однако, если вы попытаетесь оптимизировать код, который выполняется 60 раз в секунду, 1 мс — это уже много.

3) проверьте, стоит ли результат затраченной работы

Часто вам приходится проводить большой рефакторинг или много печатать, перевешивает ли прирост производительности затраченное вами время?

Является ли get настолько медленным, что вы можете привести аргумент в пользу предложений if-else, несмотря на очень плохую читаемость?

Я бы сказал, что нет. используйте _.get (если только вы не планируете запускать это несколько сотен раз в секунду).