В своей книге Р. Мартин утверждает, что функции необходимо предъявлять всего два требования:
1. они должны быть компактными
2. они должны быть еще более компактными
Т.е. функция должна выполнять лишь одну операцию. Функции, выполняющие несколько операций необходимо дробить на несколько.
Тогда вопрос: А как же накладные расходы на вызов функции? Ведь вместо того, чтобы продолжить вычисления, теперь нам надо заполнить стек и передать управление по адресу, а по возвращению очистить стек. Т.е. улучшая читабельность кода мы уменьшаем производительность системы. Да и как то меня учили, что в функцию необходимо выносить лишь тот код, который может быть вызван из нескольких участков программы. И какой тогда смысл выносить в отдельную функцию код, который будет вызываться только в одном месте?
Здравствуйте, dosik, Вы писали:
D>Т.е. функция должна выполнять лишь одну операцию. Функции, выполняющие несколько операций необходимо дробить на несколько. D>Тогда вопрос: А как же накладные расходы на вызов функции?
Это дело компилятора. Сейчас компилятор много чего без тебя инлайнит.
Да и вообще — кому сейчас нужно ловить настолько мелких блох?
Чай не времена 286-х!
Здравствуйте, dosik, Вы писали:
D>Т.е. функция должна выполнять лишь одну операцию. Функции, выполняющие несколько операций необходимо дробить на несколько.
Спорное утверждение. Нужно решать в каждом конкретном случае, что лучше.
D>Тогда вопрос: А как же накладные расходы на вызов функции? Ведь вместо того, чтобы продолжить вычисления, теперь нам надо заполнить стек и передать управление по адресу, а по возвращению очистить стек.
Как уже написали, в современных ЯВУ это забота компилятора. Он лучше человека разберётся, что куда инлайнить.
D>Т.е. улучшая читабельность кода мы уменьшаем производительность системы.
Вот это и есть цель: улучшить читаемость
D>Да и как то меня учили, что в функцию необходимо выносить лишь тот код, который может быть вызван из нескольких участков программы. И какой тогда смысл выносить в отдельную функцию код, который будет вызываться только в одном месте?
Неправильно, видимо, учили. Есть такое понятие: "самодокументируемый код".
Чем писать несколько строк кода и к ним комментарий, скажем, "// Check permissions", лучше вынести этот код в отдельный метод с названием CheckPermissions(). Комментарий станет ненужным, не сможет потерять свою актуальность, и, в случае, если функция понадобится в другом месте, коментарий там также не потребуется.
Здравствуйте, dosik, Вы писали:
D>В своей книге Р. Мартин утверждает, что функции необходимо предъявлять всего два требования: D>1. они должны быть компактными D>2. они должны быть еще более компактными D>Т.е. функция должна выполнять лишь одну операцию. Функции, выполняющие несколько операций необходимо дробить на несколько. D>Тогда вопрос: А как же накладные расходы на вызов функции?
1. Я так подозреваю, что значительная часть целевой аудитории подобных книг пишет программы, в которых производительность не является критичным фактором (не важно, секунду или две будет считаться бизнес-логика, всё равно всё в конечном итоге упирается в БД).
2. Надо стараться не переборщить с "компактизированием", т.к. не всегда разбиение фунции на меньшие улучшает читаемость. Если не ошибаюсь, как раз у Мартина в самом начале, в качестве примера, из понятной программы на 20 строк делается монстр из 5 классов.
Здравствуйте, dosik, Вы писали: D>Тогда вопрос: А как же накладные расходы на вызов функции? Ведь вместо того, чтобы продолжить вычисления, теперь нам надо заполнить стек и передать управление по адресу, а по возвращению очистить стек. Т.е. улучшая читабельность кода мы уменьшаем производительность системы.
вообще, даже когда ты пишешь на ассемблере, у тебя всегда есть выбор между ф-ией и макросом. современные копиляторы умеют сами оценивать накладные расходы на вызов и решать, что лучше — инлайнить код или реально вызывать его.
D>Да и как то меня учили, что в функцию необходимо выносить лишь тот код, который может быть вызван из нескольких участков программы. И какой тогда смысл выносить в отдельную функцию код, который будет вызываться только в одном месте?
это рекомендации где-то годов 70ых-80. после людям пришло понимание, что программы большие и сложные и соверешнно бесполезные, если в них нельзя разобраться. сначала они начали писать комменты, а потом поняли, что можно давать имена ф-ия так, чтобы по ним было все понятно и без комментов.
любой человек, который до сих пор пишет ф-ии в столбик строк по 30, вставляя туда комменты просто неквалифицирован и его нельзя допускать до написания реального кода
Здравствуйте, dosik, Вы писали:
D>Тогда вопрос: А как же накладные расходы на вызов функции? Ведь вместо того, чтобы продолжить вычисления, теперь нам надо заполнить стек и передать управление по адресу, а по возвращению очистить стек. Т.е. улучшая читабельность кода мы уменьшаем производительность системы. Да и как то меня учили, что в функцию необходимо выносить лишь тот код, который может быть вызван из нескольких участков программы. И какой тогда смысл выносить в отдельную функцию код, который будет вызываться только в одном месте?
Ну, есть такая точка зрения, что если выбирать между эффективностью кода и понятностью кода, понятность важнее. Заметим к слову, что даже в программе, в которой эффективкость важна, обычно это касается 10% кода, а остальной код вызывается редко, или упирается во ввод-вывод, а не процессор, и т.п. Кроме того, компилятор часто догадывается поинлайнить функции. И еще, вызов может быть даже дешевле инлайна, поскольку разделение кода на небольшие функции может облегчить жизнь той части компилятора, которая распределяет регистры.
Другой вопрос, что разбиение кода на мелкие функции далеко не всегда облегчает его понимание. Поэтому к этому совету надо относиться критически.
Отвечу сам себе, поскольку основная масса ответов идентична — не бери на себя работу компилятора, пиши удобочитаемый код, а компилятор сам все заинлайнит.
А что тогда с высоконагруженными приложениями, где кроме как на себя, даже на компилятор положиться нельзя?
Здравствуйте, dosik, Вы писали: D>Отвечу сам себе, поскольку основная масса ответов идентична — не бери на себя работу компилятора, пиши удобочитаемый код, а компилятор сам все заинлайнит. D>А что тогда с высоконагруженными приложениями, где кроме как на себя, даже на компилятор положиться нельзя?
они оптимизированы только в воображении менеджмента и еще может быть hr. в основном работают очень неэффективно
Здравствуйте, __kot2, Вы писали:
__>любой человек, который до сих пор пишет ф-ии в столбик строк по 30, вставляя туда комменты просто неквалифицирован и его нельзя допускать до написания реального кода
30 это много?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
В первую очередь код должен быть как можно более читаемым. Если ради этого нужно жертвовать производительностью — надо делать это не задумываясь. И только когда производительность программы станет неудовлетворительной, нужно произвести минимальные ухудшения, чтобы улучшить производительность до приемлемой. Поэтому такими вещами, как накладные расходы на вызовы функций, заморачиваться не нужно.
Есть проекты, в которых производительность важна до той степени, что их части приходится писать на языке ассемблера. Но это очень редкие случаи и погоды они не делают. В 99% случаев именно так.
Здравствуйте, dosik, Вы писали:
D>Тогда вопрос: А как же накладные расходы на вызов функции? Ведь вместо того, чтобы продолжить вычисления, теперь нам надо заполнить стек и передать управление по адресу, а по возвращению очистить стек. Т.е. улучшая читабельность кода мы уменьшаем производительность системы.
О такой мелочёвке нужно можно заботиться только после того как ты отпрофилировал программу. До профайлинга нужно написать удобочитаемый и легко изменяемый код.
Когда после профилировки ты выяснишь, что у тебя какой-нибудь Math.Max() не заинлайнен компилятором, и существует милиард его вызовов, и что от милиарда вызовов никуда не уйти (а именно в этом бОльшая часть оптимизаций и заключается), тогда и заинлайнишь — не раньше.
В ином случае ты рискуешь во время написания отловить блох, а в итоге получить O(n!) там, где должен быть O(n log(n)).
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, T4r4sB, Вы писали: TB>Здравствуйте, __kot2, Вы писали: __>>любой человек, который до сих пор пишет ф-ии в столбик строк по 30, вставляя туда комменты просто неквалифицирован и его нельзя допускать до написания реального кода TB>30 это много?
слова много и мало они тут неправильные
у меня был знакомый, он тоже писал, писал, потом когда думал, что уже много, создавал новый файл и начинал писать туда. так люди какают, а не пишут программы
функция должна делать одно дело. 30 строк — очень редко когда это что-то прямо одно такое. бывает, но редко. чаще всего там все-таки несколько действий, каждое из которых имеет смысла выделить в отдельную ф-ию.
если все равно непонятно что означает "одно действие", то для начала создайте блоки минимальной видимости переменных, типа
void see_you()
{
int size = get_size();
if size > ...
do_something(size)
//а здесь ниже уже нигде size не ипользуется и идет код типа
call_something()
do_some_stuff()
for ( x)
{
if ( x > 10)
{
print_something();
x += 2;
}
superman = new superman()
betment = new betment()
fight(superman, betment)
}
}
то разумно, чтобы случайно не наткнуться на конфликт имен взять верхий блок в скобки и записать в стиле
void see_you()
{
{
int size = get_size();
if size > ...
do_something(size)
}
call_something()
do_some_stuff()
for ( x : xs)
{
if ( x > 10)
{
print_something();
x += 2;
}
{
superman = new superman()
betment = new betment()
fight(superman, betment)
}
}
}
после этого берете и все что внутри скобочек выделяете в отдельные ф-ии (за исключением, может, пары разумных исключений) и получаете код вроде
void see_you()
{
adjust_size()
call_something()
do_some_stuff()
for ( x : xs)
{
if ( time_to_so_it(x))
do_it();
betment_superman_fight();
}
}
причем я не говорю о рефакторинге, никто не мешает так писать сразу. и, разумеется, именя ф-ий не должны называться do_stuff() а должны описывать что конкретно за стафф мы ду.
Здравствуйте, __kot2, Вы писали:
__>функция должна делать одно дело. 30 строк — очень редко когда это что-то прямо одно такое. бывает, но редко. чаще всего там все-таки несколько действий, каждое из которых имеет смысла выделить в отдельную ф-ию.
Да скорее задолбаешься контекст функции передавать в подфункции.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Я не думаю, что можно набрать 5-10 секунд такими блохами.
Десять миллиардов команд чтобы переключить канал?
Я не думаю, что это от того, что функции короткие.
Здравствуйте, dosik, Вы писали:
D>Здравствуйте, alpha21264, Вы писали:
A>>Да и вообще — кому сейчас нужно ловить настолько мелких блох? A>>Чай не времена 286-х!
D>Ну как сказать ))) Сегодня ты пишешь клиентские десктопные приложения, а завтра на тебе, и ты уже ARM программист, и там рассказывай про блох )))
Не чини, пока не сломалось. Вот когда будешь писать для АРМ-а вот тогда... Всё равно дело будет не в блохах.
Здравствуйте, T4r4sB, Вы писали: TB>Здравствуйте, __kot2, Вы писали: __>>функция должна делать одно дело. 30 строк — очень редко когда это что-то прямо одно такое. бывает, но редко. чаще всего там все-таки несколько действий, каждое из которых имеет смысла выделить в отдельную ф-ию. TB>Да скорее задолбаешься контекст функции передавать в подфункции.
контекст должен быть сгруппирован в структуры
Здравствуйте, dosik, Вы писали:
D>А что тогда с высоконагруженными приложениями, где кроме как на себя, даже на компилятор положиться нельзя?
А что такое "высоконагруженые приложения", пример можно? Или вы чисто теоретически интересуетесь?
Здравствуйте, __kot2, Вы писали:
__>любой человек, который до сих пор пишет ф-ии в столбик строк по 30, вставляя туда комменты просто неквалифицирован и его нельзя допускать до написания реального кода
Считаю такое мнение признаком недопереквалифицированности.
Если нам не помогут, то мы тоже никого не пощадим.