Які хороші стратегії для покращення серійної продуктивності мого коду?


66

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

Припустимо, я оцінив компроміс продуктивності та читабельності / повторної використання / ремонтопридатності програмного забезпечення, над яким я працюю, і вирішив, що настав час піти на ефективність. Припустимо також, що я знаю, що в мене немає кращого алгоритму для моєї проблеми (щодо flop / s та пропускної здатності пам’яті). Ви також можете припустити, що база мого коду є мовою низького рівня, такою як C, C ++ або Fortran. Нарешті, припустимо, що в коді не існує паралелізму, або що нас цікавить лише ефективність на одному ядрі.

Які найважливіші речі спробувати спершу? Як дізнатись, скільки я можу отримати?

Відповіді:


66

Перш за все, як наголосили майстер та Ден , профілювання є важливим. Я особисто використовую підсилювач VTune від Intel в Linux, оскільки він дає мені дуже тонкий огляд того, де витрачався час на те, щоб робити.

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

  • Місцезнаходження пам'яті : чи дані, які читаються / використовуються разом, також зберігаються разом, або ви збираєте шматочки та шматки тут і там?

  • Вирівнювання пам’яті : чи ваші парні парні фактично вирівняні на 4 байти? Як ти запакував structs? Щоб бути педантичним, використовуйте posix_memalignзамість malloc.

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

  • Векторизація : Ні, не замислюйтеся з ручним кодованим асемблером. gccпропонує векторні типи, які автоматично переводяться на SSE / AltiVec / будь-які інструкції.

  • Паралелізм на рівні інструкцій : Син векторизації. Якщо деякі часто повторювані обчислення не векторизуються добре, ви можете спробувати накопичити вхідні значення та обчислити кілька значень одночасно. Це схоже на розкручування циклу. Тут ви використовуєте те, що ваш процесор, як правило, має більше однієї одиниці з плаваючою комою на ядро.

  • Арифметична точність : чи справді вам потрібна арифметика подвійної точності у всьому, що ви робите? Наприклад, якщо ви обчислюєте виправлення в ітерації Ньютона, зазвичай вам не потрібні всі цифри, які ви обчислюєте. Для більш поглибленої дискусії дивіться цей документ.

Деякі з цих хитрощів використовуються в daxpy_cvec цій темі. Сказавши, що якщо ви використовуєте Fortran (не мова низького рівня в моїх книгах), ви будете мати дуже мало контролю над більшістю цих "хитрощів".

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

Оновлення

Минув час, як я написав це, і не помітив, що це стало такою популярною відповіддю. З цієї причини я хочу додати один важливий момент:

  • Поговоріть із місцевим комп'ютерним науковцем : Чи не було б здорово, якби існувала дисципліна, яка стосувалася виключно створення алгоритмів та / або обчислень більш ефективними / витонченими / паралельними, і ми могли б усі запитати їх поради? Добре, що існує така дисципліна: Інформатика. Швидше за все, у вашому закладі навіть є цілий відділ, присвячений цьому. Поговоріть із цими хлопцями.

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

На мій досвід, як комп'ютерний вчений (CS), хитрість полягає в тому, щоб отримати як очікування, так і спілкування.

Expectation -wise, КО тільки допоможе вам , якщо він / вона думає , що ваша проблема цікава. Це в значній мірі виключає спроби оптимізувати / векторизувати / паралелізувати фрагмент коду, який ви написали, але насправді не прокоментували, для проблеми, яку вони не розуміють. CS зазвичай більше зацікавлені в основній проблемі, наприклад, алгоритмах, які використовуються для її вирішення. Не дайте їм свого рішення , не передайте їм свою проблему .

Крім того, будьте готові до того, що ЦС скаже, що " ця проблема вже вирішена ", і просто дайте посилання на документ. Порада: прочитайте цей документ і, якщо він дійсно стосується вашої проблеми, застосуйте будь-який алгоритм, який він пропонує. Це не самовільний контрабандист, це лише КС, який вам допоміг. Не ображайтесь, пам’ятайте: Якщо проблема не є обчислювально цікавою, тобто вона вже вирішена, і рішення, показане як оптимальне, вони над нею не працюватимуть, тим більше кодуйте її для вас.

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

Наприклад, я зараз працюю з низкою обчислювальних космологів над написанням кращої версії їх імітаційного коду, заснованого на SPH та Multipoles . Минуло близько трьох зустрічей, щоб перестати говорити з точки зору темної матерії та галактики галактики (так?) Та провести до основи обчислення, тобто, що їм потрібно знайти всіх сусідів у заданому радіусі кожної частинки, обчислити деякі кількість над ними, а потім знову перебігайте всі згадані сусіди і застосуйте цю кількість в деяких інших обчисленнях. Потім перемістіть частинки або хоча б деякі з них, і зробіть це все заново. Розумієте, хоча перші можуть бути неймовірно цікавими (це!), Останній - це те, що мені потрібно зрозуміти, щоб почати думати про алгоритми.

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


4
Оскільки інструменти для профілювання йдуть, я б не забув про valgrind .
GertVdE

1
Я згоден з вами Педро, коли оптимізована програма - це як гоночний автомобіль F1, вже близький до оптимального. Програми, які я бачу на практиці, як наукові, так і ні, часто більше схожі на Cadillac Coupe DeVilles. Для отримання реальної продуктивності тонни жиру можна відрізати. Після цього циклічне гоління починає вражати свій крок.
Майк Данлаве

1
@MikeDunlavey: Повністю згоден. Я додав оновлення до своєї відповіді, щоб вирішити більше алгоритмічно пов'язаних питань.
Педро

1
@MikeDunlavey, я являюсь CS фольклорний :)
Pedro

2
Я це продемонстрував у бесіді в Массачусетсі Лоуелл. Це була демонстрація в прямому ефірі, що показувала всі етапи прискорення 730x. Я думаю, що один професор отримав бал, з півдесятка.
Майк Данлаве

38

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

Я використовую метод - випадкова пауза . Ось кілька прискорень, які він знайшов для мене:

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

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

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

  • Рутини для множення або перетворення матриць або прийняття холеського перетворення, як правило, містять символьні аргументи, що визначають параметри, такі як "U" або "L" для верхнього або нижнього трикутника. Знову ж таки, вони є для зручності. Що я знайшов, так як мої матриці були не дуже великими, підпрограми витрачали більше половини свого часу на виклик підпрограми для порівняння символів, щоб просто розшифрувати параметри. Написання спеціальних версій найдорожчих математичних процедур дало значну швидкість.

Якщо я можу просто розгорнути останнє: рутинне множення матриць DGEMM викликає LSAME для декодування його аргументів символів. Дивлячись на інклюзивний відсотковий час (єдину статистику, на яку варто звернути увагу), профілі, які вважаються "хорошими", могли б показати DGEMM, використовуючи якийсь відсоток загального часу, наприклад, 80%, а LSAME використовуючи деякий відсоток загального часу, як 50%. Дивлячись на колишнього, ви б спокусилися сказати: "ну це має бути сильно оптимізовано, тому я не можу з цим зробити". Дивлячись на останнє, ви б спокусилися сказати: "А? Що це все? Це просто маленька рутина. Цей профілер повинен помилятися!"

Це не помиляється, це просто не сказати тобі, що потрібно знати. Що випадкове пауза показує, що DGEMM знаходиться на 80% зразків стека, а LSAME - на 50%. (Для виявлення цього вам не потрібно багато зразків. 10, як правило, безліч.) Більше того, на багатьох із цих зразків DGEMM перебуває у виклику LSAME з декількох різних рядків коду.

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

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

ДОДАТО: Отже, щоб відповісти на два ваші останні запитання:

Які найважливіші речі спробувати спершу?

Візьміть 10-20 зразків стеків, і не просто їх підсумовуйте, розумійте, що кожен з них говорить. Зробіть це першим, останнім та між ними. (Немає "спробувати", молодий Скайуокер.)

Як дізнатись, скільки я можу отримати?

xβ(s+1,(ns)+1)sn1/(1x)n=10s=5x
введіть тут опис зображення
xx

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

(s+1)/(n+2)=3/22=13.6%.) Нижня крива на наступному графіку - це її розподіл:

введіть тут опис зображення

Поміркуйте, якщо ми взяли цілих 40 зразків (більше, ніж я коли-небудь мав за один раз) і побачили лише проблему на двох з них. Орієнтовна вартість (режим) цієї проблеми становить 5%, як показано на більш високій кривій.

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

Існує куди більш серйозний ризик - "помилковий негатив". Це коли є проблема, але вона не знайдена. (Сприяє цьому "упередженість підтвердження", коли відсутність доказів трактується як доказ відсутності.)

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

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


Є ще один момент, який потрібно зробити ще раз. Питання Педро про помилкові позитиви.

Він зауважив, що можуть виникнути труднощі при переході до невеликих проблем у високооптимізованому коді. (Для мене невелика проблема, яка становить 5% або менше всього часу.)

Оскільки цілком можливо побудувати програму, яка є абсолютно оптимальною, за винятком 5%, цей пункт можна вирішити лише емпірично, як у цій відповіді . Для узагальнення з емпіричного досвіду це виглядає так:

Програма, як написано, зазвичай містить кілька можливостей для оптимізації. (Ми можемо їх назвати "проблемами", але вони часто є абсолютно хорошим кодом, просто здатні до значного вдосконалення.) Ця діаграма ілюструє штучну програму, яка займає деякий проміжок часу (скажімо, 100), і містить проблеми A, B, C, ... що, знайшовши та виправити, економить 30%, 21% тощо від початкових 100-х.

введіть тут опис зображення

Зауважте, що проблема F коштує 5% від початкового часу, тому її "мало" і її важко знайти без 40 і більше зразків.

Однак перші 10 зразків легко знаходять проблему А. ** Коли це буде виправлено, програма займає лише 70-і, для прискорення 100/70 = 1,43x. Це не тільки робить програму швидшою, але збільшує, за цим співвідношенням, відсотки, взяті за інші проблеми. Наприклад, проблема B спочатку займала 21-е, що становило 21% від загальної кількості, але після вилучення А, В займає 21-е з 70-х, або 30%, тому його легше знайти, коли весь процес повторюється.

Коли процес повторювались п’ять разів, тепер час виконання становить 16,8s, з яких проблема F - 30%, а не 5%, тому 10 зразків знаходять це легко.

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

Якщо проблеми від A до F виявлені та усунені, швидкість дорівнює 100 / 11,8 = 8,5x. Якщо один із них пропущений, наприклад D, то прискорення становить лише 100 / (11,8 + 10,3) = 4,5х. Ось ціна, сплачена за хибні негативи.

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

2/0.3=6.671 - pbinom(1, numberOfSamples, sizeOfProblem)1 - pbinom(1, 20, 0.3) = 0.9923627

xβ(s+1,(ns)+1)nsy1/(1x)xyy1Розподіл BetaPrime Я моделював це за допомогою 2 мільйонів зразків, дотримуючись такої поведінки:

         distribution of speedup
               ratio y

 s, n    5%-ile  95%-ile  mean
 2, 2    1.58    59.30   32.36
 2, 3    1.33    10.25    4.00
 2, 4    1.23     5.28    2.50
 2, 5    1.18     3.69    2.00
 2,10    1.09     1.89    1.37
 2,20    1.04     1.37    1.17
 2,40    1.02     1.17    1.08

 3, 3    1.90    78.34   42.94
 3, 4    1.52    13.10    5.00
 3, 5    1.37     6.53    3.00
 3,10    1.16     2.29    1.57
 3,20    1.07     1.49    1.24
 3,40    1.04     1.22    1.11

 4, 4    2.22    98.02   52.36
 4, 5    1.72    15.95    6.00
 4,10    1.25     2.86    1.83
 4,20    1.11     1.62    1.31
 4,40    1.05     1.26    1.14

 5, 5    2.54   117.27   64.29
 5,10    1.37     3.69    2.20
 5,20    1.15     1.78    1.40
 5,40    1.07     1.31    1.17

(n+1)/(ns)s=ny

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

введіть тут опис зображення


1
Гм ... Чи не ви отримуєте саме цю інформацію, дивлячись на графіки викликів профілерів або резюме типу "знизу вгору", передбачені VTune?
Педро

2
@Pedro: Якщо тільки. У вибірці стека (& пов'язаних змінних) кодується вся причина того, що приріст часу витрачається. Ви не можете позбутися цього, якщо не знаєте, на що витрачаються. Деякі проблеми можна знайти з обмеженою інформацією, але не з кожною . Якщо ви отримуєте лише деякі з них, але не кожну, то проблеми, які у вас не виникають, блокують вас від подальших скорочень. Перевірте тут і тут .
Майк Данлаве

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

@Pedro: Просто продовжуйте брати зразки, поки не з’явиться щось, що можна виправити на декількох зразках. Бета-дистрибутив говорить про те, наскільки це може зекономити, якщо вам все одно, але якщо ви боїтеся отримати не меншу швидкість, ніж це показує, пам’ятайте, що ви кидаєте шанс, що це також може бути більше (і це правильно перекошене ). Більшу небезпеку, підсумовуючи профілі, - хибні негативи . Може виникнути проблема, але ви просто сподіваєтесь, що ваша інтуїція буде нюхати її, коли профайлер дуже не конкретний щодо того, де він може бути.
Майк Данлаве

@ Педро: Єдина моя слабкість, яку я знаю, - це коли, дивлячись на час знімка, ви не можете зрозуміти, на що витрачається цей час, наприклад, якщо це просто обробка асинхронних подій, де ховається запитувач, або асинхронні протоколи. Для отримання більш "нормального" коду покажіть мені "хорошого" профайлера, і я покажу вам проблему, з якою у вас проблеми або просто не вдається знайти (змушуючи вас потрапити назад на ваші помилкові розумні рамки) Як правило, спосіб побудови такої проблеми полягає в тому, щоб переконатись, що цілі, яка служить, не можна розшифрувати локально. І таких проблем багато в програмному забезпеченні.
Майк Данлаве

23

Ви маєте не тільки інтимні знання про ваш компілятор , але й інтимні знання про вашу цільову архітектуру та операційну систему .

Що може вплинути на продуктивність?

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

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

Навіть якщо цільова архітектура не змінюється, зміни на низькому рівні в операційній системі також можуть вплинути на продуктивність. Патчі для пом’якшення Spectre та Meltdown мали величезний вплив на деякі навантаження, тому вони можуть змусити переоцінити ваші оптимізації.

Як можна оптимізувати код?

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

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

Подальше читання

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

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


8

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


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

1
Домовились. Алгоритми та структура даних можуть покращити О (10) до О (100). Однак для кількох проблем, обмежених обчисленням (наприклад, у розрахунках молекулярної динаміки, астрофізиці, обробці зображень і відео в режимі реального часу, фінансах), високо налагоджений критичний цикл може означати 3-х до 10-кратний швидший загальний час роботи програми.
fcruz

Я бачив погано впорядковані вкладені петлі у «виробничих» кодах значного розміру. Крім того, я думаю, ти маєш рацію.
dmckee

8

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

Linux

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

Apple OS X

Можливо, ви захочете спробувати Shark . Він доступний на веб-сайті для розробників Apple у розділі Завантаження> Інструменти для розробників> CHUD 4.6.2, старішу версію тут . CHUD також містить інші інструменти для профілювання, такі як BigTop frontend, інструмент пошуку індексу PMC, профілер рівня функцій Saturn та безліч інших команд. Shark буде поставлятися з версією командного рядка.


Профіль +1? Так, певним чином ... Це набагато краще, ніж здогадуватися, але ось перелік питань, які стосуються особливо gprof та багатьох інших користувачів.
Майк Данлаве

Чи Shark - стара команда в OS X? Більше тут . Чи потрібно використовувати гірський лев інструменти?
hhh

@hhh: Це був графічний інтерфейс GUI для Mac, хоча, схоже, він більше не підтримується. Я не програмував на яблучній машині з тих пір, як написав цю відповідь, тому не можу вам дуже допомогти.
Дан

1
Він доступний на веб-сайті розробника Apple у розділі Завантаження> Інструменти для розробників> CHUD 4.6.2. Старіша версія тут, і вона містить всі види профілювання - на жаль, ця установка не вдається: "Зверніться до виробника", жодної ідеї про помилку. Акулу було вийнято з Xcode, очевидно, після Лева, а пізніше було повернуто на сайт Apple Dev після того, як він був безкоштовним інструментом у MacUpdate.
hhh

@hhh: Ви здаєтесь більш кваліфікованими, щоб відповісти на це, ніж я. Не соромтесь відредагувати мою відповідь, щоб оновити її, або напишіть свою.
Дан

7

Що стосується того, яку кількість результатів ви можете отримати, візьміть результати профайлювання вашого коду і скажімо, що ви визначите фрагмент, який займає частку часу "p". Якщо ви мали б покращити продуктивність цього твору лише коефіцієнтом "s", загальна швидкість буде 1 / ((1-p) + p / s). Тому ви можете максимально збільшити свою швидкість в 1 / (1-p). Сподіваємось, у вас є райони високого р! Це еквівалент Закону Амдаля щодо серійної оптимізації.


5

Оптимізацію коду потрібно робити обережно. Припустимо також, що ви вже налагодили код. Ви можете заощадити багато часу, дотримуючись певних пріоритетів, а саме:

  1. Використовуйте високооптимізовані (або професійно оптимізовані) бібліотеки, де це можливо. Деякі приклади можуть включати бібліотеки FFTW, OpenBlas, Intel MKL, NAG тощо. Якщо ви не дуже талановиті (наприклад, розробник GotoBLAS), ви, ймовірно, не можете перемогти професіоналів.

  2. Скористайтеся профілером (досить багато в наступному списку вже названо в цій темі - Intel Tune, valgrind, gprof, gcov тощо), щоб дізнатися, які частини вашого коду займають найбільше часу. Немає сенсу витрачати час на оптимізацію частин коду, які рідко викликаються.

  3. З результатів профайлера подивіться частину вашого коду, яка зайняла найбільше часу. Визначте, яка природа вашого алгоритму - це пов'язаний процесор чи пам'ять? Кожен вимагає різного набору методів оптимізації. Якщо у вас багато пропусків кешу, пам'ять може бути вузьким місцем - процесор витрачає цикли годин, очікуючи, коли пам'ять стане доступною. Подумайте, чи вписується цикл у кеш L1 / L2 / L3 вашої системи. Якщо у вашому циклі є твердження "якщо", перевірте, чи говорить профайлер щось про непередбачення гілки? Що таке галузеве непередбачуване покарання вашої системи? До речі, ви можете отримати дані про непередбачення галузевих довідників із посібників з оптимізації Intel [1]. Зауважте, що штраф за непередбачувану галузь залежить від процесора, як ви побачите в посібнику Intel.

  4. Нарешті, вирішіть проблеми, виявлені профілером. Тут уже обговорювались низку методик. Також доступні ряд хороших, надійних, всебічних ресурсів для оптимізації. Якщо назвати лише два, є посібник з оптимізації Intel [1] та п'ять посібників з оптимізації від Agner Fog [2]. Зауважте, що вам можуть не знадобитися робити деякі речі, якщо компілятор це вже робить - наприклад, розкручування циклу, вирівнювання пам'яті тощо. Уважно прочитайте документацію компілятора.

Список літератури:

[1] Посібник з оптимізації архітектури Intel 64 та IA-32: http://www.intel.sg/content/dam/doc/manual/64-ia-32-architectures-optimization-manual.pdf

[2] Agner Fog, "Ресурси оптимізації програмного забезпечення": http://www.agner.org/optimize/

  • "Оптимізація програмного забезпечення на C ++: Посібник з оптимізації для платформ Windows, Linux та Mac"
  • "Оптимізація підпрограм на мові збірки: Посібник з оптимізації для платформ x86"
  • "Мікроархітектура процесорів Intel, AMD та VIA: Посібник з оптимізації для програмістів збирання та розробників компіляторів"
  • "Таблиці інструкцій: Списки затримок інструкцій, пропускної спроможності та розбоїв мікро-операцій для процесорів Intel, AMD та VIA"
  • "Виклики умов для різних компіляторів C ++ та операційних систем"

3

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

У будь-якому випадку наводимо два приклади (якщо ви ще їх не читали) про те, як було покращено ефективність роботи (для неструктурованих проблем ІС).

Серійний текст : Див. Другу половину реферату та суміжного тексту.

Паралель : Особливо етап ініціалізації, в розділі 4.2.


3

Це, можливо, більше метавідповіді, ніж відповіді ...

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

Багато корисних порад про те, що @Pedro відпускає, можна реалізувати, скоригувавши компіляцію, а не програму.


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

1

Простий спосіб профілювання програми (в Linux) - це використання perfв statрежимі. Найпростіший спосіб - це просто запустити його

perf stat ./my_program args ...

і це дасть вам купу корисної статистики ефективності:

Performance counter stats for './simd_test1':

     3884.559489 task-clock                #    1.000 CPUs utilized
              18 context-switches          #    0.005 K/sec
               0 cpu-migrations            #    0.000 K/sec
             383 page-faults               #    0.099 K/sec
  10,911,904,779 cycles                    #    2.809 GHz
 <not supported> stalled-cycles-frontend
 <not supported> stalled-cycles-backend
  14,346,983,161 instructions              #    1.31  insns per cycle
   2,143,017,630 branches                  #  551.676 M/sec
          28,892 branch-misses             #    0.00% of all branches

     3.885986246 seconds time elapsed

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

Ви також можете спробувати, perf record ./my_program; perf reportякий простий спосіб профілювати. Прочитайте сторінки чоловіка, щоб дізнатися більше.

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