Подтвердить что ты не робот

"Функциональные вызовы дороги" против "Сохранить функции небольшие",

С одной стороны, я читаю или слышу, что "вызовы функций дороги" и что они влияют на эффективность (например, на технологию Google Николаса Закаса говорить).

С другой стороны, однако, считается, что функции/методы лучше всего держать в курсе и должны выполнять только одну задачу, как это принято в здесь.

Мне что-то не хватает, или эти два совета не противоречат друг другу? Есть ли какое-то правило, которое позволяет поддерживать баланс, подобный дзен?

4b9b3361

Ответ 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, запустите временная производительность линейно снижается с размером стека вызовов, несмотря на то, что выглядит концептуально чистым.