русс | укр

Мови програмуванняВідео уроки php mysqlПаскальСіАсемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование


Linux Unix Алгоритмічні мови Архітектура мікроконтролерів Введення в розробку розподілених інформаційних систем Дискретна математика Інформаційне обслуговування користувачів Інформація та моделювання в управлінні виробництвом Комп'ютерна графіка Лекції


Інформація про пенальті


Дата додавання: 2014-10-07; переглядів: 903.


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

Повернемося до попереднього питання: чому покажчик pswd завантажується так довго? І при яких саме обставинах завантаження змінної "підскакує" зі своїх звичайних семи до вісімдесятьох тактів? Зважаючи на всі, процесору щось не сподобалося й він обклав ці дві машинні команди "штрафом" (penalty), на годину пригальмувавши їхнє виконання. Чи можемо мі довідатися, коли й за який "провину" це відбулося? Не прибігаючи до повної емуляції процесора — чи навряд (хоча сучасні процесори x86 з деякими обмеженнями дозволяють одержати цю інформацію й так).

Гранди комп'ютерної індустрії — Intel і AMD уже давно випустили власні профіліровщики, що містять повноцінні емулятори процесорів, що випускаються ними,, ці програми дозволяють візуалізувать нюанси виконання кожної машинної інструкції.

Просто клацніть по рядку mov ecx, DWORD PTR [ ebp-28] і профіліровщик VTune видасть всю, наявну в нього інформацію (лістинг 1.5).

Лістинг 1.5. Висновок програми VTune додаткової інформації про виконання інструкції

decoder minimum clocks = 1 ; МІНІМАЛЬНИЙ ГОДИНА ДЕКОДУВАННЯ – 1 ТАКТ

Decoder Average Clocks = 8.7 ; Ефективний година декодування – 8,7 тактів

Decoder Maximum Clocks = 86 ; Максимальний година декодування – 86 тактів

 

Retirement Minimum Clocks = 0, ; МІНІМАЛЬНИЙ ГОДИНА ЗАВЕРШЕННЯ – 0 ТАКТІВ

Retirement Average Clocks = 7.3 ; Ефективний година завершення – 7,3 такти

Retirement Maximum Clocks = 80 ; Максимальний година завершення – 80 тактів

total cycles = 80 (00,65%) ; УСЬОГО ЧАСУ ВИКОНАННЯ – 80 ТАКТІВ

micro-ops for this instruction = 1 ; КІЛ-У МІКРООПЕРАЦІЙ В ІНСТРУКЦІЇ – 1

The instruction had to wait 0 cycles for it's sources to be ready

("ІНСТРУКЦІЯ ЧЕКАЛА 0 ТАКТІВ ПОКИ ПІДГОТОВЛЯЛИСЯ ЇЇ ОПЕРАНДЫ, Т.Е. ПОПРОСТУ ВОНА ЇХ НЕ ЧЕКАЛА ЗОВСІМ")

Dynamic Penalty: IC_miss

The instruction was not in the instruction cache, so the processor loads the instruction from the L2 cache or main memory.

("ІНСТРУКЦІЯ БУЛА ВІДСУТНЯ В КОДОВОМУ КЕШ, І ПРОЦЕСОР БУВ ЗМУШЕНИЙ ЗАВАНТАЖУВАТИ ЇЇ З КЕШ ІНШОГО РІВНЯ АБО ОСНОВНОЇ ОПЕРАТИВНОЇ ПАМ'ЯТІ")

Occurances = 1 ; Таке траплялося 1 раз

Dynamic Penalty: L2instr_miss

The instruction was not in the L2 cache, so the processor loads the instruction from main memory.

("ІНСТРУКЦІЯ БУЛА ВІДСУТНЯ В КЕШ ІНШОГО РІВНЯ Й ПРОЦЕСОР БУВ ЗМУШЕНИЙ ЗАВАНТАЖУВАТИ ЇЇ З ОСНОВНОЇ ОПЕРАТИВНОЇ ПАМ'ЯТІ")

Occurances = 1 ; Таке траплялося 1 раз

Dynamic Penalty: Store_addr_unknown

The load instruction stalls due to the address calculation of the previous store instruction.

(" ІНСТРУКЦІЯ, ЩОЗАВАНТАЖУЄ, ПРОСТОЮВАЛА З ТІЄЇ ЗАПОДІЙ, ЩО АДРЕСИ ДЖЕРЕЛА РОЗРАХОВУВАВСЯ ПОПЕРЕДНЬОЮ ІНСТРУКЦІЄЮ ЗАПИСУ")

Occurances = 10 ; Таке траплялося 10 разів

Так, здається, наше розслідування перетворюється в самого справжнього детектива. Якщо прикласти до отриманого результату навіть самий скромний арифметичний апарат, вийде повна нісенітниця й повна розбіжність "дебіту із кредитом". Аналізуйте самостійно. Повний година виконання інструкції — 80 тактів, причому, відомо, що вона виконувалася 11 разів (див. у лістингу 1.3 колонкові count звіту профіліровщика). А найгірший година виконання інструкції склало…80тактів! А найгірший година декодування й зовсім — 86! Тобто, гірший година декодування інструкції перевищує загальний година її виконання й при цьому в десяти ітераціях вона ще може простоювати як мінімум один такт за кожну ітерацію через зайнятість блоку розрахунку адреса.

Причина такої невідповідності полягає у відносності самого поняття години. Ви думаєте година відносно тільки в Эйнштейна? Аж ніяк! У конвеєрних процесорах (зокрема процесорах Pentium і AMD K6/Athlon) поняття "години виконання інструкції" взагалі не існує в принципі. У силу того, що кілька інструкцій можуть виконуватися паралельно, просте алгебраїчне підсумовування години їхнього виконання дасть значно більший результат, ніж виконання займає в дійсності.

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

При великій кількості ітерацій (а при "живому" виконанні програми воно й впрямь велико) годиною початкового завантаження можна й зневажити, алі…Стоп! Адже профіліровщик виконав тіло даного циклу всього 11 разів, у результаті чого середній година виконання цієї інструкції склало 7,3 тактів, що зовсім не відповідає реальній дійсності!

Виявляється, це зовсім не "гаряча" крапка! І отут зробленого німа чого оптимізувати. Якщо мі збільшимо кількість прогонів профіліровщика хоча б у чотири рази, середній година виконання нашої інструкції понизитися до 1,8 тактів і вона виявиться одним із самих "холодних" місць програми! Точніше — це взагалі абсолютний нуль, оскільки ефективний година виконання даної інструкції — нуль тактів (тобто вона завершується одночасно з попередньою машинною командою). Словом, мі "промахнулися" по повній програмі.

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

Лістинг 1.6. Демонстрація коду, деякі ділянки якого аналізуються профіліровщиком відносно невелику кількість разів, що спотворює результат профілювання

while((++pswd[p])>'z') // даний цикл прогоняется профилировщиком 1.000 разів

{

pswd[p] = '!'; // дана інструкція прогоняется всього 11 разів

}

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

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

Дійсно, — рядок pswd[p] = '!' — це перший рядок тіла циклу, що одержує керування кожні 0x59 ітерацій, що набагато перевершує "проникливість" динамічного алгоритму пророкування розгалужень, що використовується процесором для запобігання зупинки обчислювального конвеєра.

Отже, дане розгалуження завжди передвіщається помилково й виконання цієї інструкції процесору доводити починати з нуля. А процесорний конвеєр — довгий. Поки він заповнитися…Властиво, отут винувата зовсім не команда mov edx, dword ptr [ebp+0ch] — будь-яка інша команда на її місці виконувалася б настільки ж непродуктивно! "Паяльна грілка, до червона нагріває" цю крапку програми, перебуває зовсім в іншому місці!

Піднімемо курсор ледве вище, на інструкцію умовного переходу попередній цій команді, і двічі клацнемо мишкою. Профіліровщик VTune видасть наступну інформацію:

decoder minimum clocks = 0 ; МІНІМАЛЬНИЙ ГОДИНА ДЕКОДУВАННЯ – 0 ТАКТІВ

Decoder Average Clocks = 0 ; Ефективний година декодування – 0 тактів

Decoder Maximum Clocks = 4 ; Максимальний година декодування – 4 такти

retirement average clocks = 1 ; ЕФЕКТИВНИЙ ГОДИНА ЗАВЕРШЕННЯ – 1 ТАКТ

total cycles = 1011 (08,20%) ; УСЬОГО ЧАСУ ВИКОНАННЯ – 1010 ТАКТІВ (8,2%)

micro-ops for this instruction = 1 ; КІЛ-У МІКРООПЕРАЦІЙ В ІНСТРУКЦІЇ – 1

The instruction had to wait (8,11.1,113) cycles for it's sources to be ready

("ЦЯ ІНСТРУКЦІЯ ЧЕКАЛА МІНІМАЛЬНО 8, МАКСИМАЛЬНО 113, А В ОСНОВНОМУ 11,1 ТАКТІВ ПОКИ ЇЇ ОПЕРАНДЫ НЕ БУЛИ ГОТОВІ")

Dynamic Penalty: BTB_Miss_Penalty ; ШТРАФ ТИПУ btb_miss_penalty

This instruction stalls because the branch was mispredicted.

("ЦЯ ІНСТРУКЦІЯ ПРОСТОЮВАЛА ТОМУ ЩО РОЗГАЛУЖЕННЯ НЕ БУЛО ПЕРЕДВІЩЕНО")

Occurances = 13 ; Таке траплялося 13 разів

Наша гіпотеза повністю підтверджується. Це розгалуження тринадцять разів передвіщалося неправильно, про що VTune і свідчить! Постій, як тринадцять?! Адже тіло циклу виконується тільки одинадцять! Так, правильно, одинадцять. Алі адже процесор наперед цього не знав, і двічі намагався передати на нього керування, і лише потім, "побачивши", що жодне із двох пророкувань не збулося, "плюнувши і заприсяг", що ніколи-ніколи не поставити свої фішки на цю гілку.


<== попередня лекція | наступна лекція ==>
Питомна година виконання | Визначення кількості викликів


Онлайн система числення Калькулятор онлайн звичайний Науковий калькулятор онлайн