Чи впливає модульне програмування на час обчислення?


19

Усі кажуть, що я повинен зробити свій код модульним, але чи не менш ефективно він, якщо я використовую більше викликів методів, а не менших, але більших методів? У чому полягає різниця в Java, C або C ++?

Я розумію, що це легше редагувати, читати та розуміти, особливо в групі. Тож чи є втрати часу на обчислення незначними порівняно з вигодами від чистоти коду?


2
Питання полягає в тому, скільки часу знадобиться час обробки, який ви заощадите, пройдете час, витрачений на більш складне обслуговування. Відповідь на це повністю залежить від вашої заявки.
Blrfl

2
Багато хороших запитань породжують певну ступінь думок, заснованих на досвіді експертів, але відповіді на це питання, як правило, базуються майже на думках, а не на фактах, посиланнях або конкретних знаннях.
гнат

10
Варто також зазначити, що обчислювальний штраф для виклику функції чи методу настільки мізерний, що навіть у дуже великій програмі з великою кількістю функцій та викликів методів, здійснення цих викликів навіть не займає графік.
greyfade

1
@greyfade: Це справедливо для прямих стрибків, але додатковий непрямий прогнозований стрибок може коштувати, наприклад, ~ 3% від загального часу роботи програми (лише число програми, яку я нещодавно перевірив - вона, можливо, не була представницькою). Залежно від вашої області ви можете чи не вважаєте його значущим, але він зареєструвався на діаграмі (і це, звичайно, є частково ортогональним по модульності).
Maciej Piechotka

4
Передчасна оптимізація - корінь усього зла. Лінійний код трохи швидший, ніж модульний код. Модульний код набагато швидший, ніж код спагетті. Якщо ви орієнтуєтесь на лінійний код без дуже (ДУЖЕ) ретельного проекту всієї справи, ви отримаєте код спагетті, я гарантую це.
СФ.

Відповіді:


46

Так, це не має значення.

Комп'ютери - це невтомні, майже ідеальні двигуни, що працюють на швидкостях, абсолютно не порівнянних з мізками. Незважаючи на те, що час виклику функції додає до часу виконання програми вимірювану кількість часу, це як ніщо порівняно з додатковим часом, необхідним мозку наступної людини, що займається кодом, коли їм доводиться роз'єднувати нечитабельну процедуру навіть почати розуміти, як з цим працювати. Ви можете спробувати обчислити жарт - припустимо, що ваш код потрібно підтримувати лише один раз , і це додасть лише півгодини часу, необхідного для того, щоб хтось змирився з кодом. Візьміть тактову частоту процесора і обчисліть: скільки разів доведеться запустити код, щоб навіть мріяти про компенсацію цього?

Коротше кажучи, змилосерджуватися над процесором повністю, абсолютно помилково 99,99% часу. Для рідкісних решти випадків використовуйте профілі. Ви НЕ передбачає , що ви можете визначити ті випадки , - ви не можете.


2
Хоча я згоден, що більшість часу це передчасна оптимізація, я вважаю, що ваш аргумент із часом поганий. Час у різних ситуаціях відносний, і ви не можете просто розрахувати так, як це робили.
Хонза Брабек

28
+1 просто за вираз "пошкодуєш процесор", оскільки він так чудово підкреслює неправильну голову в передчасній оптимізації.
Майкл Боргвардт

4
Пов’язані між собою: Наскільки швидко бувають комп'ютери в повсякденному виразі? Вийти зі свого шляху, щоб уникнути кількох функціональних викликів "швидкості" - це як вийти зі свого шляху, щоб заощадити когось на одну хвилину всього за все життя . Якщо коротко: це навіть не варто часу, який потрібно розглянути.
BlueRaja - Danny Pflughoeft

7
+1 за те, що красномовно продає те, чого багато не вистачає, але ви забули додати найважливішу вагу до балансу, що робить жаління процесора ще більш серйозним: нехтування втраченими грошима та витраченим часом на те, що працювало на одноразовому обслуговуванні; якщо це зайняло 1 секунду, все ще є куди більш підступна раковина. Небезпека помилок . Чим більш заплутаним і важким для подальшого технічного обслуговування змінювати код, тим більше ризик його впроваджувати в ньому помилки, що може спричинити непереборно великі неминучі витрати від ризику для користувачів, а будь-яка помилка спричиняє подальше обслуговування (що може спричинити помилки ...)
Джиммі Хоффа

Моя стара вчителька казала: "Якщо ти думаєш, передчасно оптимізуєш чи ні, зупинися тут. Якщо це був правильний вибір, ти це знав би".
Вен

22

Це залежить.

У льодовиково-повільному світі, який є веб-програмуванням, де все відбувається зі швидкістю людини, програмування, що займає важкі методи, де вартість виклику методу порівнянна або перевищує витрати на обробку методом, мабуть, не має значення. .

У світі програмування вбудованих систем та обробників переривань для швидкісних переривань це, безумовно, має значення. У такому середовищі звичні моделі "доступ до пам'яті дешевий" і "процесор нескінченно швидкий" руйнується. Я бачив, що відбувається, коли програміст, орієнтований на мейнфрейм, пише свій перший високошвидкісний обробник переривань. Це було не красиво

Кілька років тому я займався нерекурсивним 8-ти канальним підключенням, розфарбовуючи зображення FLIR у режимі реального часу, на тому, що був на той час гідним процесором. Перша спроба використовувала виклик підпрограми, і накладні виклику підпрограми з'їли процесор живим. (4 дзвінки PER PIXEL x 64K пікселів на кадр x 30 кадрів в секунду = ви це з'ясуєте). Друга спроба перетворила підпрограму на макрос С, не втрачаючи читабельності, і все було трояндами.

Ви повинні дивитись ТВЕРДО на те, що ви робите, і на середовище, в якому ви будете це робити.


Пам’ятайте, що більшість сучасних проблем програмування найкраще вирішується з великою кількістю об'єктно-орієнтованих методів. Ви дізнаєтесь, коли перебуваєте у вбудованому системному середовищі.
Кевін - Відновіть Моніку

4
+1, але з застереженням: зазвичай так трапляється, що оптимізуючий компілятор часто виконує кращу роботу з вбудовування, розгортання циклу та здійснення скалярної та векторної оптимізації, ніж людина. Програмісту все ще потрібно добре знати мову, компілятор і машину, щоб скористатися цим, тому це не магія.
detly

2
Це правда, але ви оптимізували свій код після того, як ви написали логіку та профілювали її, щоб знайти проблематичну частину у вашому алгоритмі. Ви почали не кодувати ефективність, а читабельність. І макроси допоможуть вам зберегти читати код.
Uwe Plonus

2
Навіть у програмуванні вбудованих систем, почніть із написання чіткого правильного коду та лише почніть оптимізувати його після необхідності та керуючись профілюванням . Інакше надто просто вкласти багато роботи, що не має реального впливу на продуктивність / розмір коду, і це просто робить джерело важким для розуміння.
Стипендіати Дональ

1
@detly: Так і ні. Сучасні компілятори ВЗАГАЛЬНО можуть зробити кращу роботу в межах меж, які надає мова. Людський програміст знає, чи застосовані мовою обмеження у конкретному випадку. Наприклад, компілятори Cn і C ++ вимагаються мовними стандартами, щоб програміст міг зробити певну дуже патологічну річ і генерувати код, який все одно працює. Програміст може знати, що він недостатньо божевільний, не дурний або авантюрний, щоб зробити це, і зробити оптимізацію, яка б інакше була небезпечною, оскільки він знає, що в цьому випадку вони безпечні.
Джон Р. Стром

11

Перш за все: програми вищою мовою - це для того, щоб їх читали люди, а не машини.

Тому запишіть програми, щоб ви їх зрозуміли. Не думайте про ефективність (якщо у вас серйозно проблеми із виконанням, тоді профіліруйте свою програму та підвищуйте продуктивність там, де це потрібно).

Навіть якщо це правда, що виклик методу чи функції вимагає певних витрат, це не має значення. Сьогодні компілятори повинні мати можливість складати ваш код на ефективній машинній мові, щоб згенерований код був ефективним для цільової архітектури. Використовуйте перемикачі оптимізації вашого компілятора, щоб отримати ефективний код.


5
Я б сказав, що ми повинні писати програми таким чином, щоб інші розуміли їх.
Bartlomiej Lewandowski

Перша людина, якій доводиться читати програму, - це сам розробник. Ось чому я написав вам . Якщо інші люди також можуть прочитати програму, це приємно, але (на мої очі) не обов’язково (в першу чергу). Якщо ви працюєте в команді, то інші люди також повинні розуміти програму, але мій намір полягав у тому, що людина повинна читати програму, а не комп'ютери.
Uwe Plonus

5

Як правило, коли ви мали б велику функцію і розділите її на безліч менших, ці менші будуть накреслені, оскільки єдиний недолік вбудовування (повторення тих же вказівок занадто багато) у цьому випадку не має значення. Це означає, що ваш код буде діяти так, ніби ви написали одну велику функцію.

Якщо вони з певних причин не накреслені, і це стає проблемою продуктивності, тоді слід розглянути можливість вручну вкладати. Не всі програми мають мережеві форми CRUD з величезними внутрішніми затримками.


2

Мабуть, немає розрахункових витрат. Зазвичай компілятори / СТІ протягом останніх 10-20 років або близько займаються функцією, яка вписується ідеально. Для C / C ++, як правило, це обмежується функціями "нездійсненний характер" (тобто визначення функції доступно для компілятора під час компіляції - тобто це в заголовку того ж файлу), але сучасні методи LTO долають це.

Якщо вам доведеться витратити час на оптимізацію, залежить від сфери, в якій ви працюєте. Якщо ви маєте справу з "звичайним" додатком, який витрачав більшу частину часу на очікування введення, напевно, вам не варто турбуватися про оптимізацію, якщо додаток "не відчуває себе повільним".

Навіть у таких випадках слід сконцентруватися на багатьох речах, перш ніж робити мікрооптимізацію:

  • Де проблеми? Зазвичай людям важко знайти гарячі точки, оскільки ми читаємо вихідний код по-різному. У нас різне співвідношення часу роботи, і ми виконуємо їх послідовно, тоді як сучасні процесори цього не роблять .
  • Чи потрібно робити якийсь розрахунок щоразу? Наприклад, якщо ви змінюєте один параметр з тисяч, ви можете обчислити лише частину, на яку впливає, а не цілу модель.
  • Ви використовуєте оптимальний алгоритм? Перехід від O(n)до O(log n)може мати набагато більший вплив, ніж усе, чого ви могли досягти за допомогою мікрооптимізації.
  • Чи використовуєте ви належні структури? Скажіть, ви використовуєте те, Listколи вам потрібно, HashSetщоб у вас були O(n)пошуки, коли ви могли O(1).
  • Чи ефективно ви використовуєте паралелізм? В даний час навіть мобільні телефони можуть мати 4 ядра і більше, можливо, спокусливо використовувати нитки. Однак вони не є срібною кулею, оскільки вони коштують на синхронізацію (не кажучи вже про те, що якщо проблема пов'язана з пам'яттю, то все одно немає сенсу).

Навіть якщо ви вирішили, що вам потрібно виконати мікрооптимізацію (що практично означає, що ваше програмне забезпечення використовується в HPC, вбудоване або просто використовується дуже великою кількістю людей - інакше додаткові витрати на обслуговування перевищують комп’ютерні витрати часу) щоб визначити гарячі точки (ядра), які ви хочете пришвидшити. Але тоді вам, мабуть, слід:

  1. Точно знайте платформу, над якою ви працюєте
  2. Точно знайте компілятор, над яким ви працюєте, і яку оптимізацію він здатний робити, і як написати ідіоматичний код, щоб увімкнути цю оптимізацію
  3. Подумайте про схеми доступу до пам'яті та про те, скільки ви можете вмістити в кеш (точний розмір якого ви знаєте з пункту 1).
  4. Тоді, якщо ви пов'язані з обчисленнями, подумайте про реорганізацію обчислень, щоб зберегти обчислення

Як остаточне зауваження. Зазвичай єдиною проблемою, що виникає з викликами методів, є непрямі стрибки (віртуальні методи), які не були передбачені передбачувачем гілки (на жаль, непрямий стрибок - це важкий випадок). Однак:

  • У Java є JIT, який у багатьох випадках може передбачити тип класу, а отже, заздалегідь ціль стрибка, і тому в гарячих точках у вас не повинно виникнути багато проблем.
  • Компілятори C ++ часто виконують програмний аналіз і принаймні в деяких випадках можуть передбачити ціль під час компіляції.
  • В обох випадках, коли ціль було передбачено, вкладка повинна працювати. Якщо компілятор не міг виконати вкладені шанси, ми теж не могли б.

0

Моя відповідь, ймовірно, не надто розшириться на існуючі відповіді, але я відчуваю, що мої два центи можуть бути корисними.

По-перше; так, для модульності ви зазвичай відмовляєтеся від певного рівня часу на виконання. Якщо все записати в асемблерний код, ви отримаєте найкращу швидкість. Це сказало ...

Ви знаєте YouTube? Ймовірно, чи найпопулярніший веб-сайт із пропускною здатністю, чи другий за Netflix? Вони пишуть велику частину свого коду на Python, що є високомодульною мовою, не зовсім побудованою для найвищої продуктивності.

Річ у тім, що коли щось йде не так, і користувачі скаржаться на повільне завантаження відео, не так багато сценаріїв, де ця повільність в кінцевому рахунку була б віднесена до повільної швидкості виконання Python. Однак швидка перекомпіляція Python та його модульна здатність пробувати нові речі без перевірки типу, ймовірно, дозволять інженерам досить швидко налагоджувати те, що відбувається не так ("Нічого. Наш новий стажер написав цикл, який робить новий підзапит SQL" для ВСЯКОГО результату. ") або (" О, Firefox застарів цей старий формат заголовка кешування; і вони створили бібліотеку Python, щоб легко встановити новий ")

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


0

Так і ні. Як інші відзначили програму для читабельності спочатку, потім для ефективності. Однак є стандартні практики, які є і читабельними, і ефективними. Більшість кодів запускається досить рідко, і ви не отримаєте великої переваги від оптимізації.

Java може вбудовувати менші виклики функцій, тому є мало причин уникати написання функцій. Оптимізатори прагнуть краще працювати з більш простим для читання кодом. Існують дослідження, які показують ярлики, які теоретично повинні працювати швидше, фактично зайняти більше часу. Компілятор JIT, швидше за все, працює краще, код менше, і часто виконуються фрагменти можна ідентифікувати та оптимізувати. Я не пробував цього, але сподіваюся, що одна велика функція, яку досить рідко називають не збирається.

Це, швидше за все, не стосується Java, але в одному дослідженні встановлено, що більші функції дійсно працювали повільніше через необхідність використання іншої моделі пам'яті. Це було специфічно для обладнання та оптимізатора. Для менших модулів використовувались інструкції, які працювали на сторінці пам'яті. Вони були швидшими та меншими, ніж необхідні інструкції, коли функція не підходила до сторінки.

Є випадки, коли варто оптимізувати код, але, як правило, потрібно профайлювати код, щоб визначити, де це. Я вважаю, що це часто не той код, якого я очікував.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.