Зробіть його робочим, зробіть його чистим, зробіть його ТВЕРДОМО, а потім зробіть його так швидко, як це потрібно для роботи .
Це має бути нормальний порядок речей. Ваш найперший пріоритет - це зробити щось, що пройде тести прийняття, які позбавляться вимог. Це ваш перший пріоритет, оскільки це перший пріоритет вашого клієнта; задоволення функціональних вимог у терміни розробки. Наступним пріоритетом є написання чистого, читабельного коду, який легко зрозуміти і, таким чином, може підтримуватися вашим нащадком без будь-яких 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 ядрах, виглядає лінійним. Отже, якщо ви впевнені, що ви досягли нижчої межі складності для типу алгоритму, який ви пишете, шукайте способи "розділити та перемогти".
- Це швидко, коли це досить швидко - Якщо ви не вручаєте пакувальну машину для націлювання на певний чіп, завжди можна щось отримати. Однак, якщо ви НЕ хочете складати пакувальну машину, ви завжди повинні мати на увазі те, що клієнт назвав би "досить хорошим". Знову ж таки, "передчасна оптимізація - корінь усього зла"; коли ваш клієнт зателефонує йому досить швидко, ви закінчите, поки він не подумає, що це досить швидко.