Питання інженера початкового рівня щодо управління пам'яттю


9

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

Просте запитання, яке я представив колезі-колезі, відповів "я зосереджуюсь на неправильних речах". Хоча я поважаю цього колегу, я не погоджуюся з тим, що на цьому варто "зосередитись".

Тут був код (у VB), а за ним - питання.

Примітка: Функція GenerateAlert () повертає ціле число.

Dim alertID as Integer = GenerateAlert()
_errorDictionary.Add(argErrorID, NewErrorInfo(Now(), alertID))    

проти ...

 _errorDictionary.Add(argErrorID, New ErrorInfo(Now(), GenerateAlert()))

Я спочатку написав останнє і переписав його "Dim alarID", щоб комусь було легше читати. Але ось у мене виникло питання і питання:

Якщо написати це за допомогою Dim AlertID, це фактично займе більше пам'яті; обмеженість, але більше, і чи повинен цей метод називатися багато разів, чи це може призвести до проблеми? Як .NET буде обробляти цей об’єкт AlertID. Поза. .NET слід обробляти об’єкт після використання вручну (близько кінця підрозділу).

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


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

6
Я зовсім не впевнений, що це дійсно буде використовувати більше пам’яті з анонімним цілим числом проти названого цілого. У будь-якому випадку це справді передчасна оптимізація. Якщо вам потрібно турбуватися про ефективність на цьому рівні (я майже впевнений, що ви цього не робите), можливо, вам знадобиться C ++ замість C #. Добре зрозуміти проблеми продуктивності та те, що відбувається під кришкою, але це маленьке дерево у великому лісі.
psr

5
Іменоване ціле анонімне ціле число не використовуватиме більше пам’яті, тим більше, що анонімне ціле число є лише названим цілим числом, яке ВАС не назвав (компілятор все-таки повинен його назвати). Щонайменше, назване ціле число матиме іншу область, щоб воно могло жити довше. Анонімне ціле число проживе лише до тих пір, поки потрібен буде метод, іменований проживе до тих пір, поки його потребує його батько.
Джоель Етертон

Подивимось ... Якщо Integer - це клас, він буде виділений на купі. Локальна змінна (найімовірніше, у стеці) буде посилатися на неї. Посилання буде передано на об'єкт D error. Якщо під час виконання обчислення посилань (або таких), тоді, коли більше немає посилань, він (об'єкт) буде розміщений з купи. Все, що знаходиться в стеці, автоматично «розміщується», коли метод виходить. Якщо це примітив, він (швидше за все) опиниться на стеці.
Павло

Ваш колега мав рацію: питання, порушене вашим запитанням, мало стосуватися не оптимізації, а читабельності .
haylem

Відповіді:


25

"Передчасна оптимізація - корінь усього зла (або, принаймні, більшості) в програмуванні." - Дональд Кнут

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

Чи варто заглиблюватися в ці теми продуктивності та оптимізації? Абсолютно, але не на долар вашої компанії, якщо це зайве.


1
На кого ще має бути долар? Ваш роботодавець скоріше отримує переваги від підвищення кваліфікації, ніж ви.
Marcin

Предмети, які безпосередньо не сприяють вашому поточному завданню? Вам слід займатися цими справами самостійно. Якби я сів і дослідив кожен предмет CompSci, який викликав мою цікавість протягом дня, я б нічого не зробив. Ось для чого мої вечори.
Крістофер Берман

2
Дивно. У когось із нас є особисте життя, і, як я кажу, роботодавець виграє насамперед дослідження. Головне - насправді не витрачати на це цілий день.
Marcin

6
Добре вам. Це насправді не робить це універсальним правилом. Крім того, якщо ви відмовляєте своїх працівників від навчання на роботі, все, що ви зробили, - це відштовхувати їх від навчання, і заохочуєте їх знайти іншого роботодавця, який фактично платить за розвиток персоналу.
Marcin

2
Я розумію думки, зазначені у вищезазначених коментарях; Я хотів би зазначити, що я попросив під час обідньої перерви. :). Ще раз дякую всім за ваш внесок тут і на всій сторінці Stack Exchange; це неоціненно!
Шон Гоббс

5

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

Один із складних переходів, які я мав, - перехід від використання C і MASM до програмування в класичному VB ще в 90-х. Мене звикли оптимізувати все для розміру та швидкості. Мені довелося здебільшого відмовитися від цього мислення і дозволити VB це робити, щоб бути ефективним.


5

Як завжди говорив мій колега:

  1. Зроби так, щоб він працював
  2. Виправити всі помилки, щоб вона працювала бездоганно
  3. Зробіть це твердим
  4. Застосовуйте оптимізацію, якщо вона працює повільно

Іншими словами, завжди пам’ятайте про KISS (тримайте це просто нерозумно). Тому що надмірна інженерія, переосмислення певної логіки коду може стати проблемою для зміни логіки наступного разу. Однак зберігати код чистим і простим - це завжди хороша практика .

Однак за часом та досвідом ви краще дізнаєтесь, який код пахне та потребує оптимізації досить скоро.


3

Якщо слід написати це за допомогою Dim AlertID

Читання є важливим. Однак у вашому прикладі я не впевнений, що ви дійсно робите речі, які можна читати. GenerateAlert () має гарне ім'я, і ​​це не додає багато шуму. Мабуть, краще використовувати ваш час.

насправді це займе більше пам’яті;

Я підозрюю, що це не так. Це порівняно пряма оптимізація для компілятора.

чи слід називати цей метод багато разів, чи це може призвести до проблеми?

Використання локальної змінної в якості посередника не впливає на сміттєзбірник. Якщо GenerateAlert () new запам'ятовує пам'ять, то це матиме значення. Але це матиме значення незалежно від локальної змінної чи ні.

Як .NET буде обробляти цей об’єкт AlertID.

AlertID не є об’єктом. Результатом GenerateAlert () є об'єкт. AlertID - це змінна, яка, якщо це локальна змінна, просто простір, пов'язаний з методом відстеження речей.

Поза. .NET слід обробляти об'єкт після використання вручну

Це складніше питання, яке залежить від контексту та семантики власності на примірник, наданий GenerateAlert (). Загалом, будь-який створений екземпляр повинен видалити його. Ваша програма, ймовірно, виглядатиме суттєво інакше, якби вона розроблялася з урахуванням ручного управління пам'яттю.

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

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


2

Зробіть його робочим, зробіть його чистим, зробіть його ТВЕРДОМО, а потім зробіть його так швидко, як це потрібно для роботи .

Це має бути нормальний порядок речей. Ваш найперший пріоритет - це зробити щось, що пройде тести прийняття, які позбавляться вимог. Це ваш перший пріоритет, оскільки це перший пріоритет вашого клієнта; задоволення функціональних вимог у терміни розробки. Наступним пріоритетом є написання чистого, читабельного коду, який легко зрозуміти і, таким чином, може підтримуватися вашим нащадком без будь-яких WTF, коли це стане необхідним (майже ніколи не виникає питання "якщо"; вам чи комусь після вас доведеться йти повернутися назад і щось змінити / виправити). Третій пріоритет - примусити код дотримуватися методології SOLID (або GRASP, якщо вам зручніше), яка вводить код у модульні, багаторазові, змінні шматки, які знову сприяють обслуговуванню (вони не тільки можуть зрозуміти, що ви робили, і чому, але є чисті лінії, уздовж яких я можу хірургічно видалити та замінити шматочки коду). Останній пріоритет - продуктивність; якщо код достатньо важливий, щоб він відповідав характеристикам продуктивності, він майже напевно є досить важливим, щоб він був правильним, чистим і вперше ТВЕРДО.

Наголошуючи Крістофера (і Дональда Кнута), "передчасна оптимізація - корінь усього зла". Крім того, такі оптимізації, які ви розглядаєте, є незначними (посилання на ваш новий об’єкт буде створено на стеці, незалежно від того, даєте йому ім'я у вихідному коді чи ні) і типу, який не може спричинити різниці в компільованому Іл. Імена змінних не переносяться в ІР, тому оскільки ви декларуєте змінну безпосередньо перед її першим (і, мабуть, єдиним) використанням, я б став би на кілька пивних грошей, що ІЛ однаковий між вашими двома прикладами. Отже, ваш колега має право на 100%; ви шукаєте в неправильному місці, якщо ви дивитесь на названу змінну проти вбудованого екземпляра, щоб щось оптимізувати.

Мікрооптимізація в .NET майже ніколи не варта (я кажу про 99,99% випадків). C / C ++, можливо, ЯКЩО ви знаєте, що ви робите. Працюючи в середовищі .NET, ви вже досить далеко від металу апаратного забезпечення, що має значні накладні витрати у виконанні коду. Отже, враховуючи, що ви вже знаходитесь в середовищі, яке вказує на те, що ви відмовилися від швидкості руху, і замість цього шукаєте написати "правильний" код, якщо щось у середовищі .NET не працює досить швидко, або його складність полягає в занадто високий, або вам слід подумати про його паралелізацію. Ось деякі основні вказівки, які слід дотримуватися для оптимізації; Я гарантую вам, що ваша продуктивність в оптимізації (швидкість, отримана за витрачений час) зросте:

  • Зміна форми функції має значення більше, ніж зміна коефіцієнтів - складність WRT Big-Oh, ви можете зменшити наполовину кількість кроків, які необхідно виконати в алгоритмі N 2 , і у вас все ще є алгоритм квадратичної складності, хоча він виконується в вдвічі менше часу. Якщо це нижня межа складності для даного типу проблем, так і нехай буде, але якщо для цієї ж проблеми є NlogN, лінійне або логарифмічне рішення, ви отримаєте більше, перемикаючи алгоритми для зменшення складності, ніж оптимізуючи ту, яка є у вас.
  • Тільки тому, що ви не бачите складності, це не означає, що це не коштує вам. Багато хто з найелегантніших вкладишів у слові спрацьовують жахливо (наприклад, проста шашка Regex - це функція експоненціальної складності, при цьому ефективна просте оцінювання, що включає ділення числа на всі прості числа, менші, ніж його квадратний корінь, є на порядок O (Nlog (sqrt (N))). Linq - це чудова бібліотека, оскільки вона спрощує код, але, на відміну від двигуна SQL, .Net компілятор не намагатиметься знайти найефективніший спосіб виконання вашого запиту. Ви повинні знати, що станеться при використанні методу, і, отже, чому метод може бути швидшим, якщо розмістити його раніше (або пізніше) у ланцюзі під час створення ті самі результати.
  • У OTOH майже завжди існує компроміс між складністю джерела та складністю виконання - SelectionSort дуже легко здійснити; ви, ймовірно, могли зробити це в 10LOC або менше. MergeSort трохи складніший, тим більше Quicksort, а RadixSort - тим більше. Але, оскільки алгоритми збільшують складність кодування (і, таким чином, "випереджає" час розробки), вони зменшують складність виконання; MergeSort і QuickSort - це NlogN, а RadixSort взагалі вважається лінійним (технічно це NlogM, де M найбільша кількість у N).
  • Перервіться швидко - якщо є чек, який можна зробити недорого, що, ймовірно, є правдою і означає, що ви можете рухатися далі, зробіть цю перевірку спочатку. Якщо ваш алгоритм, наприклад, піклується лише про числа, які закінчуються на 1, 2 або 3, найімовірнішим випадком (з урахуванням повністю випадкових даних) є число, яке закінчується якоюсь іншою цифрою, тому перевірте, що число НЕ закінчується 1, 2 або 3, перш ніж робити перевірки, щоб побачити, чи закінчується число на 1, 2 або 3. Якщо логічний фрагмент вимагає A&B, а P (A) = 0,9, тоді як P (B) = 0,1, тоді перевірте Спочатку B, якщо тільки якщо! A ​​тоді! B (як if(myObject != null && myObject.someProperty == 1)) або B займає в 9 разів більше часу, ніж A для оцінки ( if(myObject != null && some10SecondMethodReturningBool())).
  • Не задайте жодного запитання, на яке ви вже знаєте відповідь - Якщо у вас є серія "пропускних" умов, і одна чи більше цих умов залежать від більш простої умови, яку також потрібно перевірити, ніколи не перевіряйте обидва ці незалежно. Наприклад, якщо у вас є чек, який вимагає A, і чек, який вимагає A&& B, ви повинні перевірити A, а якщо вірно, ви повинні перевірити B. Якщо! A, то! A&& B, тому навіть не турбуйтеся.
  • Чим більше разів ви щось робите, тим більше ви повинні звертати увагу на те, як це робиться - Це поширена тема в розвитку, на багатьох рівнях; в загальному сенсі розвитку, "якщо спільне завдання забирає багато часу або химерно, продовжуйте виконувати його, поки ви не будете розчаровані і достатньо обізнані, щоб придумати кращий шлях". З точки зору коду, чим більше разів буде запущений неефективний алгоритм, тим більше ви отримаєте загальну продуктивність, оптимізуючи його. Існують інструменти профілювання, які можуть приймати двійкову збірку та її налагоджувальні символи та показувати вам, обробляючи деякі випадки використання, які рядки коду були виконані найбільше. Саме ці лінії та лінії, які проводять ці лінії, - це те, на що слід звернути найбільшу увагу, тому що будь-яке підвищення ефективності, яке ви досягнете там, буде примножено.
  • Більш складний алгоритм виглядає як менш складний алгоритм, якщо ви кидаєте на нього достатню кількість обладнання . Бувають випадки, коли ви просто повинні усвідомити, що ваш алгоритм наближається до технічних обмежень системи (або його частини), на якій ви працюєте; з цього моменту, якщо це потрібно зробити швидше, ви отримаєте більше, просто запустивши його на краще обладнання. Це стосується також паралелізації; алгоритм N 2 -складності, коли він працює на N ядрах, виглядає лінійним. Отже, якщо ви впевнені, що ви досягли нижчої межі складності для типу алгоритму, який ви пишете, шукайте способи "розділити та перемогти".
  • Це швидко, коли це досить швидко - Якщо ви не вручаєте пакувальну машину для націлювання на певний чіп, завжди можна щось отримати. Однак, якщо ви НЕ хочете складати пакувальну машину, ви завжди повинні мати на увазі те, що клієнт назвав би "досить хорошим". Знову ж таки, "передчасна оптимізація - корінь усього зла"; коли ваш клієнт зателефонує йому досить швидко, ви закінчите, поки він не подумає, що це досить швидко.

0

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

Визначення "величезного", очевидно, змінюється залежно від того, якими є цільові системи.


0

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

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