"Функциональные вызовы дороги" против "Сохранить функции небольшие",
С одной стороны, я читаю или слышу, что "вызовы функций дороги" и что они влияют на эффективность (например, на технологию Google Николаса Закаса говорить).
С другой стороны, однако, считается, что функции/методы лучше всего держать в курсе и должны выполнять только одну задачу, как это принято в здесь.
Мне что-то не хватает, или эти два совета не противоречат друг другу? Есть ли какое-то правило, которое позволяет поддерживать баланс, подобный дзен?
Ответы
Ответ 1
Общее правило, применяемое ко всем языкам, следующее: сохранить как можно меньше функций (методов, процедур). Когда вы добавляете правильное именование, вы получаете очень удобный и удобочитаемый код, где вы можете легко сфокусироваться на общей картине и переходить к интересным деталям. С помощью одного огромного метода вы всегда смотрите на детали, и общая картина скрыта.
Это правило применяется конкретно к умным языкам и компилятору, которые могут делать фантастические оптимизации, такие как inlining, или обнаруживать, какие методы на самом деле не являются виртуальными, поэтому двойная отправка не требуется.
Вернуться к JavaScript - это сильно зависит от механизма JavaScript. В некоторых случаях я ожидал бы достойного двигателя встроенной функции, избегая стоимости исполнения, особенно в жестких петлях. Однако, если у вас нет проблемы с производительностью, предпочитайте более мелкие функции. Читаемость гораздо важнее.
Ответ 2
В идеальном мире, где нет ошибок (потому что код просто исправляет себя волшебным образом), а требования заморожены с первого дня, возможно, можно жить с огромными всемогущими функциями.
Но в этом мире он оказывается слишком дорогим - и не только с точки зрения "человеко-месяца". Николай Закас написал блестящую статью, описывающую большинство проблем, с которыми сталкиваются разработчики программного обеспечения в эти дни.
Переход может показаться несколько искусственным, но я считаю, что подход "одна функция - одна задача" гораздо более удобен в обслуживании и гибкости - другими словами, это то, что делает разработчиков и клиентов BOT счастливыми, в конце.
Это не значит, что вы не будете стремиться использовать как можно меньше вызовов функций: просто помните, что это не главный приоритет.
Ответ 3
Мое эмпирическое правило состоит в том, что пора сломать функцию на более мелкие куски, если она больше, чем экран, полный строк, хотя многие из моих функций, естественно, в конечном итоге несколько меньше, чем без "искусственного" разделения, И я вообще оставляю достаточно белого пространства, что даже полный экран не очень много кода.
Я пытаюсь, чтобы каждая функция выполняла только одну задачу, но тогда одна задача может быть "перерисовкой экрана", которая включает в себя ряд подзадач, реализованных в отдельных функциях, которые, в свою очередь, могут иметь свои собственные подзадачи в отдельном функции.
Начав с того, что кажется естественным (для меня) для удобочитаемости (и, следовательно, простота обслуживания), я не беспокоюсь о дорогостоящих вызовах функций, если конкретный фрагмент кода плохо работает при тестировании - тогда я бы посмотрел на вещи назад в строке (особенно в циклах, начиная с вложенных циклов). Хотя, сказав, что иногда вы просто знаете, что конкретный фрагмент кода не будет работать хорошо и переписать его, прежде чем дойти до тестирования...
Я бы избегал "преждевременной оптимизации", особенно с языками, использующими интеллектуальные компиляторы, которые могли бы выполнять те же самые оптимизации за кулисами. Когда я впервые начал С#, мне сказали, что разрыв кода в меньшие функции может быть менее дорогостоящим во время выполнения из-за того, как работает компилятор JIT.
Возвращаясь к моему одному правилу с полным экраном, в JavaScript обычно есть вложенные функции (из-за работы JS-закрытий), и это может сделать содержащую функцию дольше, чем хотелось бы, если бы я использовал другую язык, поэтому иногда конечным результатом является компромисс.
Ответ 4
Всем: у этого больше ощущается "комментарий". Признанный. Я решил использовать пространство "ответа". Пожалуйста, терпите.
@StefanoFratini: Пожалуйста, примите мою записку как свою работу. Я хочу избежать критики.
Вот два способа дальнейшего улучшения кода в вашем сообщении:
- Используйте обе половины кортежа, исходящие из process.hrtime(). Он возвращает массив [секунды, наносекунды]. Ваш код использует наносекундную часть кортежа (элемент 1), и я не могу найти, что он использует секундную часть (элемент 0).
- Будьте ясны относительно единиц.
Могу ли я соврать с моим бластером? Не знаю. Вот разработка кода Стефано. У него есть недостатки; Я не удивлюсь, если кто-нибудь скажет мне об этом. И это будет хорошо.
"use strict";
var a = function(val) { return val+1; }
var b = function(val) { return val-1; }
var c = function(val) { return val*2 }
var time = process.hrtime();
var reps = 100000000
for(var i = 0; i < reps; i++) { a(b(c(100))); }
time = process.hrtime(time)
let timeWith = time[0] + time[1]/1000000000
console.log(`Elapsed time with function calls: ${ timeWith } seconds`);
time = process.hrtime();
var tmp;
for(var i = 0; i < reps; i++) { tmp = 100*2 - 1 + 1; }
time = process.hrtime(time)
let timeWithout = time[0] + time[1]/1000000000
console.log(`Elapsed time without function calls: ${ timeWithout } seconds`);
let percentWith = 100 * timeWith / timeWithout
console.log(`\nThe time with function calls is ${ percentWith } percent\n` +
`of time without function calls.`)
console.log(`\nEach repetition with a function call used roughly ` +
`${ timeWith / reps } seconds.` +
`\nEach repetition without a function call used roughly ` +
`${ timeWithout / reps } seconds.`)
Это явно потомок кода Стефано. Результаты совсем разные.
Elapsed time with function calls: 4.671479346 seconds
Elapsed time without function calls: 0.503176535 seconds
The time with function calls is 928.397693664312 percent
of time without function calls.
Each repetition with a function call used roughly 4.671479346e-8 seconds.
Each repetition without a function call used roughly 5.0317653500000005e-9 seconds.
Как и Стефано, я использовал Win10 и Node (v6.2.0 для меня).
Я подтверждаю аргументы, что
- "Для перспективы, в наносекундах (миллиард, 1е-9), свет распространяется примерно на 12 дюймов".
- "Мы говорим только о небольшом количестве наносекунд (от 47 до 5), поэтому кто интересуется процентами?"
- "Некоторые алгоритмы производят циклические вызовы функций каждую секунду, поэтому они складываются для них".
- "Большинство из наших разработчиков не работают с этими алгоритмами, поэтому беспокоиться о количестве вызовов функций является контрпродуктивным для большинства из нас".
Я повешу свою шляпу по экономическому аргументу: Мой компьютер и тот, который перед ним стоил меньше 400 долларов США (США). Если инженер-программист зарабатывает что-то вроде $90 до $130 в час, ценность их времени для их боссов составляет соотношение одного компьютера, такого как моя, до трех или четырех часов их работы. В этой среде:
Как это сравнивается с долларами в час, которые компания теряет, когда программное обеспечение, в котором оно нуждается, перестает работать?
Как это сравнивается с потерянной доброй волей и престижем, когда платный клиент временно не может использовать сжатое программное обеспечение, созданное деловым партнером?
Там много других подобных вопросов. Я их опускаю.
Как я интерпретирую ответы: читаемость и ремонтопригодность управляются по производительности компьютера. Мой совет? Напишите первую версию своего кода соответственно. Многие люди, которых я уважаю, говорят, что короткие функции помогают.
После того, как вы закончите свой код и не понравится производительность, найдите точки заслонки. Многие люди, которых я уважаю, говорят, что эти моменты никогда не будут там, где вы ожидали бы их. Работайте, когда вы их знаете.
Итак, обе стороны правы. Некоторые.
Me? Думаю, я где-то ухожу. Два цента.
Ответ 5
Функциональные вызовы всегда дороги (особенно для циклов), а вложение не происходит так часто, как вы думаете
Двигатель V8, который поставляется с Node.js(любой версией), должен широко использовать, но с практической точки зрения эта возможность сильно ограничена.
Следующий (тривиальный) фрагмент кода доказывает мою точку (Node 4.2.1 на Win10x64)
"use strict";
var a = function(val) {
return val+1;
}
var b = function(val) {
return val-1;
}
var c = function(val) {
return val*2
}
var time = process.hrtime();
for(var i = 0; i < 100000000; i++) {
a(b(c(100)));
}
console.log("Elapsed time function calls: %j",process.hrtime(time)[1]/1e6);
time = process.hrtime();
var tmp;
for(var i = 0; i < 100000000; i++) {
tmp = 100*2 + 1 - 1;
}
console.log("Elapsed time NO function calls: %j",process.hrtime(time)[1]/1e6);
Результаты
Elapsed time function calls: 127.332373
Elapsed time NO function calls: 104.917725
+/- снижение производительности на 20%
Можно было бы ожидать, что компилятор V8 JIT будет встроить эти функции, но на самом деле a
, b
или c
можно было бы вызывать где-то еще в коде и не являются хорошим кандидатом для подхода с низким уровнем висячего фрукта. получить с V8
Я видел много кода (Java, Php, Node.js), имеющих низкую производительность в процессе производства из-за вызова метода или функции abuse: если вы пишете свой стиль Matryoshka, запустите временная производительность линейно снижается с размером стека вызовов, несмотря на то, что выглядит концептуально чистым.