Профіліровщік Quantify p>
Введення p>
Quantify,
вставляючи налагоджувальний код в бінарний текст тестового модуля, заміряє
тимчасові інтервали, які пройшли між попереднім і поточними запусками.
Отримана інформація відображається у кількох видах: табличному, графічному,
комбінованому. p>
Статистична
інформація від Quantify дозволить дізнатися які dll бібліотеки брали участь у
роботу додатку, дізнатися список всіх викликаних функцій з їх іменами,
формальними параметрами виклику й з статистичними аналізатором, що показує
скільки кожна функція виконувалася. p>
Гнучка
система фільтрів Quantify дозволяє, не захаращуючи екран зайвими висновками
(наприклад, системними викликами) дозволяє отримувати необхідну інформацію або
тільки про внутрішні, програмних виклики або тільки про зовнішні, або комбінуючи
обидва підходи. p>
Озброївшись
отриманої статистикою, розробник без праці виявить вузькі місця в
продуктивності тестової програми та усуне їх в найкоротші терміни. p>
Запуск додатків p>
p>
Малюнок
1 показує дії після вибору «File-> Run», в результаті якого можна
вибрати ім'я зовнішнього модуля і аргументи його дзвінка. p>
В
Як параметри налаштування можна вибрати метод вставки налагоджувальному коду: p>
Line. Найкращий спосіб вставки
налагоджувальному коду. Заміряється час виконання кожного рядка тестуємого
додатки. p>
Function. Те ж саме, що і для «line»,
але з виміром для виконавчі викликаються функцій. p>
Time. Здійснює збір тимчасової
інформації і на основі цього ви машинні цикли. p>
За
замовчуванням Quantify збирає статистичну інформацію в модулі тестуємого
продукту і у всіх зовнішніх бібліотеках. p>
p>
Початок
насичення тестової програми супроводжується появою вікна
інструментірованія, в якому порядково відображаються всі модулі, що викликаються
основним. Дані модулі, як говорилося вище, насичуються налагоджувальний кодом і
поміщаються в спеціальну директорію «cache» за адресою
«Rationalquantifycache». Відзначимо, що початковий запуск
інструментірванія процес тривалий, але кожний наступний виклик скорочує
загальний час очікування в силу того, що вся необхідна інформація вже є в
Кеші. p>
З
точки зору дискової ємності, файл (Кешована) налагоджування від
Quantify вдвічі довший за свого побратима без налагоджувальної інформації. P>
Аналіз інформації p>
«Run Summary» p>
Плавно
переходимо до наступної стадії тестування, власне, до збору інформації. За
закінчення процесу насичення налагоджувальний кодом модулів тестової програми,
Quantify переходить до його виконання, вироблених, звичайним чином, за одним
винятком: запис станів тестової програми продовжує проводитися в
фоновому режимі. p>
p>
Малюнок
3 демонструє фрагмент вікна «Summary», в якому проводиться запис
стану тестової програми. Причому, що дуже примітно,
тестування проводиться не тільки для простого додатка, але і для багато
поточного. В останньому випадку (див. малюнок 3), тестується кожен потік
окремо. У будь-якому випадку, навіть якщо додаток Однопотокові, то ім'я основного
(єдиного) потоку іменується як «. main_0», що видається цілком
логічним. p>
Інформаційний
графік поступово наповнюється квадратами різного кольору, що демонструють
поточний стан тестової програми. p>
Відзначимо
із деякі них: p>
Running. Початок виконання потоку; p>
Waiting IO. Очікування дій з
вводувиводу; p>
Blocked. Блокування виконання
потоку; p>
Quantify. Очікування виклику модуля
Quantify; p>
Exited. Закінчення виконання потоку.
p>
Важливий
аспект при тестуванні - отримання статистичної інформації про кількість
зовнішніх бібліотек, що викликало основну програму, а також елементарне
опис машини, на якій проводилося тестування. Останнє особливо важливо,
так як буває, що помилка проявляється тільки на процесорах строго
певної серії або виробника. Всі статистичні аспекти вирішуються
всередині вікна «Summary». p>
Наступні
два приклади показують статистичну інформацію: p>
(1) Загальний звіт - «Details»: p>
Program Name:
C: projectsaaDebugaa.exe p>
Program Arguments: p>
Working Directory:
C: projectsaaDebug p>
User Name: Alex p>
Product Version: 2002.05.00 4113 p>
Host Name: ALEX-GOLDER p>
Machine Type: Intel Pentium Pro
Model 8 Stepping 10 p>
# Processors: 1 p>
Clock Rate: 847 MHz p>
O/S Version: Windows NT 5.1.2600 p>
Physical Memory: 382 MBytes p>
PID: 0xfbc p>
Start Time: 24.04.2002 14:17:38 p>
Stop Time: 24.04.2002 14:17:52 p>
Elapsed Time: 13330 ms p>
# Measured Cycles: 191748 (0 ms) p>
# Timed Cycles: 2489329 (2 ms) p>
Dataset Size (bytes): 0x4a0001. p>
(2) «Log» p>
Quantify for Windows, p>
Copyright (C) 1993-2001 Rational
Software Corporation All rights reserved. P>
Version 2002.05.00; Build: 4113; p>
WinNT 5.1 2600 Uniprocessor Free p>
Instrumenting: p>
Far.exe 620032 bytes p>
ADVAPI32.DLL 549888 bytes p>
ADVAPI32.DLL 549888 bytes p>
USER32.DLL 561152 bytes p>
USER32.DLL 561152 bytes p>
SHELL32.DLL 8322560 bytes p>
SHELL32.DLL 8322560 bytes p>
WINSPOOL.DRV 131584 bytes p>
WINSPOOL.DRV 131584 bytes p>
MPR.DLL 55808 bytes p>
MPR.DLL 55808 bytes p>
RPCRT4.DLL 463872 bytes p>
RPCRT4.DLL 463872 bytes p>
GDI32.DLL 250880 bytes p>
GDI32.DLL 250880 bytes p>
MSVCRT.DLL 322560 bytes p>
MSVCRT.DLL 322560 bytes p>
SHLWAPI.DLL 397824 bytes p>
SHLWAPI.DLL
397824 bytes p>
Для
розробника або тестера інформація (інформаційний звіт), представлена
вище, здатна пролити світло на ті статистичні дані, які супроводжували,
а точніше, формували середу тестування. p>
Дерево викликів «Call
Graph » p>
Наступний
найцікавіший спосіб аналізу конструкції додатки - це перегляд дерева
викликів. Вікно, показане на 4 малюнку, показує лише фрагмент вікна з
діаграмою дзвінків. p>
p>
Зверніть
увагу на кількість і послідовність виклику різних модулів потоку
«Main». Жирна лінія показує найбільш тривалі гілки (що містять або
часто викликаються функції, або функції, що виконувалися довше інших). Для
демонстрації можливостей Quantify було сконструйовано простий додаток,
що складається з функції "main» і двох додаткових «recursive» і «outside» (див.
лістинг 1). p>
Лістинг 1. Приклад тестового
додатки, сконструйованому у вигляді консольного застосування з Visual Studio
6.0. Мова реалізації «С». P>
# include
"stdafx.h" p>
// Створюємо
функцію-заглушку p>
void
outside (void) p>
( p>
static
int v = 0; p>
v ++; p>
) p>
// Створюємо
рекурсивну функцію, реклама, яка 100 раз p>
int recursive (void) p>
( p>
static int i = 0; p>
int
oo; p>
outside ();// Викликаємо
функцію заглушку p>
if (i == 100) (i = 1; return
0;)// Обнуляємо лічильник і виходимо p>
i ++; p>
recursive (); p>
) p>
int main (int argc, char * argv []) p>
( p>
int i; p>
for (i = 0; i <100; i + +) recursive (); p>
// Викликаємо
100 раз рекурсивну функцію 100х100 p>
return
0; p>
) p>
Додаток
просте по суті, але дуже змістовне, так як ефективно демонструє
основні можливості Quantify. p>
В
самому початку статті ми висували вимогу, за яким розробникам не
рекомендується користуватися рекурсивними функціями. p>
Тестери
або розробники, побачивши діаграму викликів, виділять функцію, що знаходиться в
півколі, що є ознакою рекурсивного виклику (див. малюнок). p>
p>
В
залежно від того, на якій з гілок дерева, знаходиться курсор, виводиться
додаткова статистична інформація про тимчасовий доступ до виділеної
функції і до дочірніх, що йде нижче. p>
Наступний
малюнок демонструє статистику по функції «recursive». p>
p>
Більше
докладно про статистику буде розказано в наступному матеріалі. p>
Список викликів функцій «Function List» p>
Одне
з найбільш важливих статистичних вікон. Тут у табличному вигляді виводиться
статистична інформація по числу і часу роботи кожної функції. Малюнок 6
демонструє вікно з включеною сортуванням за кількістю викликів кожної функції. У
як додаткова інформація включений список формальних параметрів викликів
функцій. Подібну інформацію можна отримати тільки в тому випадку, коли
тестується модуль налагоджування кодом, до якого додається вихідний текст. p>
Одиниці
виміру тривалості роботи функцій можуть бути наступними: p>
мікросекунди;
p>
мілісекунд;
p>
Секунди;
p>
Машинні
цикли. p>
На
малюнку наведені цифри відповідні машинним циклами. p>
p>
Отримана
таблиця викликів аналізується тестером або розробником для з'ясування вузьких
місць в продуктивності програми. p>
До
жаль, для ухвалення рішення про продуктивність програми, а точніше,
продуктивності окремих його функцій можна приймати тільки розглядаючи
дане питання в комплексному розрізі. А саме, береться до уваги і число
викликів кожної функції, і середній час доступу до функції і загальний час роботи
функції, і, нарешті, те використовувалися чи при компіляції певні
специфічні настройки компілятора. p>
Це
комплексний підхід, що не припускає однозначного ради. p>
Спочатку
розглянемо опис стовпців в таблиці, що з'явилася. Хоча багато хто з пунктів і
є інтуїтивно зрозумілими, все ж спробуємо дати їм короткий опис: p>
Function. Найменування функції. Можна
висвічувати число і тип формальних параметрів виклику даної функції. p>
Calls. Число викликів. Величина
абсолютна. p>
Function Time. Загальний час
виконання всіх викликів даної функції p>
Max F Time. Максимальний час
функції p>
Module. Повний шлях до модуля з
функцією (бінарного) p>
Min F Time. Мінімальний час роботи
функції p>
Source File. Повний шлях до вихідних
текстів модуля. p>
За
будь-якого із запропонованих полів можна провести сортування в прямому і зворотному порядку,
а також накласти фільтр на певні модулі, наприклад, для перевірки тільки
внутрішніх модулів або тільки зовнішніх. p>
Виділити
зі списку вузьку функцію важко, оскільки для правильного розрахунку потрібно
брати до уваги і час роботи функції і число викликів. Причому, число
викликів не завжди може бути показником повільності функції (викликається
часто, а працює швидко). p>
Важко
давати будь-які поради з оптимізації коду, тим більше, що в цій редакції ми
не ставили перед собою подібних цілей. За теорією оптимізації написані величезні
праці, до яких ми з радістю і відправляємо читачів. p>
Можна,
звичайно, дати загальні рекомендації щодо поліпшення продуктивності коду і його
ефективності: p>
Використовувати
конструкцію «I + +» замість «I = I +1», так як компілятори транслюють першу
конструкцію в більш ефективний код. На жаль, цей ефективність прикладу
обмежена налаштуваннями використовуваного компілятора, і іноді буває рівнозначної
за швидкодією; p>
Використовувати
прийом розгортання циклів. Такий самий старий прийом оптимізації роботи
відносно простих циклів. Ефект полягає в скороченні кількості перевірок
умови лічильника, так при перевірці виконуються дуже повільні функції
мікропроцесора (функції переходу). Тобто замість коду: p>
For (i = 0; i <100; i + +) sr = sr 1; p>
Краще
писати: p>
For (i = 0; i <100; i + = 2) p>
( p>
sr ++; p>
sr ++; p>
)
p>
Використовувати
тактику відмови від Використання обчислюваного конструкцій всередині простих циклів.
Тобто, якщо мати подібний фрагмент коду: p>
for
(sr = 0; sr <1000; sr ++) p>
( p>
a [sr]
= X * y; p>
) p>
то
його краще перетворити в такий: p>
mn = x * y; p>
for (sr = 0; sr <1000; sr ++) p>
( p>
a [sr]
= Mn; p>
) p>
оскільки
ми "забрали від зайвої операції множення в простому циклі; p>
Там
де можливо при роботі з багатовимірними масивами, поводитися з ними як з
одновимірними. Тобто, якщо є необхідність у копіювання або ініціалізації,
наприклад, двовимірного масиву, то замість коду: p>
int sr [400] [400]; p>
int j, i; p>
for (i = 0; i <400; i ++) p>
for (j = 0; j <400; j ++) p>
sr [j] [i]
= 0; p>
краще
використовувати конструкцію, в якій немає вкладеного циклу: p>
int sr [400] [400]; p>
int * p = & sr [0] [0]; p>
for
(int i = 0; i <400 * 400; i ++) p>
p [sr]
= 0;// або * p + + = 0, що для
більшості компіляторів одне й теж p>
Також
при роботі з циклами вигідно використовувати злиття, коли кілька коротких
однотипних циклів зливаються разом. Подібний підхід також дає приріст у
продуктивності коду; p>
В
математичних додатках, що вимагають великих обчислень з плаваючою точкою,
або з великою кількістю дзвінків тригонометричних функцій, зручно не
проводити всі обчислення безпосередньо, а використовувати підготовлені
значення в різних таблицях, звертаючись до них як до індексів у масиві. Підхід
дуже ефективний, але, на жаль, як і багато ефективні підходи можна застосувати не
завжди; p>
Короткі
функції у класах краще оформляти вбудованими (inline); p>
В
строкових операціях, в операціях копіювання масивів краще користуватися не
власними функціями, а застосовувати для цього стандартні бібліотеки
компілятора, тому що ці функції, як правило, вже оптимізовані по
швидкодією; p>
Використовувати
команди SSL і MMX, оскільки в досить широке коло завдань вони здатні дати
прискорення роботи додатків в рази. Під такі завдання потрапляють завдання по роботі
з матрицями і векторами (арифметичні операції над матрицями та векторами); p>
Використовувати
інструкції зсуву замість множення і ділення там, де це дозволяє робити
логіка програми. Наприклад, інструкція S = S <<1 завжди ефективніше, ніж
S = S * 2; p>
Звичайно,
це далеко не повний список прийомів оптимізації коду по продуктивності і
якості. Для цього є маса інших книг. Приклади тут мають чисто
утилітарний підхід: демонстрація можливостей Quantify в плані дослідження
тимчасових характеристик коду. p>
Використовуючи
всі кошти збору і відображення, розробник поступово зможе використовувати
тільки ефективні конструкції, що підніме продуктивність на недосяжну
раніше висоту. p>
За
будь-якої функції можна вивести більш детальний звіт (див. малюнок). З нього можна
почерпнути інформацію про кількість дочірніх функцій і те, звідки вони були
зроблені. Наступний малюнок демонструє цю можливість. P>
p>
Перехід
до перегляду початкового тексту. p>
Якщо
тестовий модуль супроводжується вихідним текстом, то в Quantify є
можливість з переходу на рівень перегляду початкового тексту. За контекстного
меню можна здійснити цей перехід. Викликати функцію переходу має сенс
тільки в тому випадку, коли Quantify працює в незалежному режимі, у відриві від
середовища розробки. Малюнок демонструє даний режим. P>
Порівнювання запусків
«Compare Runs» p>
В
більшості випадків потрібно мати не тільки відомості про окремі запуски,
але й порівняння різних запусків у різних комбінаціях для прогнозування та аналізу.
Адже завжди цікаво знати швидко працює виправлена функція або повільно,
в порівнянні з тим, що було до цього. p>
Подібна
аналітична інформація дозволити мати досить чітке уявлення про те
чи знаходяться функції в прогресуючому або в регресуючим стані. p>
p>
Для
виклику модуля порівняння необхідно скористатися кнопкою (Compare Runs),
виділивши один з запусків, і вказавши на будь-якій іншій (кожен новий запуск
відображається в історії запусків на лівій частині робочого поля Quantify). p>
Для
здійснення не порожнього порівняння, як приклад, розглянутий вище, навмисно
були внесені зміни, які збільшили число викликів функцій. Дані були
збережені і переконфігурувати і знову виконані в Quantify. Результат
представлений на малюнку: p>
p>
Порівняння
запусків дозволяє проводити порівняльний аналіз між базовим запуском (base
- Те, з якого все почалося) і новим (new). p>
Результати
порівняння також залишаються в проекті Quantify і зберігаються протягом
життєвого циклу розробки проектів. p>
Нарівні
з порівнянням запуск можна скористатися підсумовуванням, натиснувши на кнопку .
Ця функція викликає просте підсумовування чисел від двох запусків. P>
Можна
порівнювати і складати також запуски разом зі зліпками (snapshot), які
дозволяють оформити поточний кількісне стан в роботі додатку у вигляді
окремого запуску. Надалі над ним можна провести будь-яку логічну
операцію. p>
Цінність
зліпків проявляється тоді, коли необхідно дізнатися число викликів кожної функції
до звершення певної події, наприклад, до входу в певний пункт
меню в тестованому додатку. p>
API p>
Це
додаткова можливість, що надається Quantify за повним управління
процесомтестування. API представляє собою набір функцій, які можна
викликати з тестової програми на розсуд розробника. p>
Для
отримання доступу до API необхідно виконати низку дій з підключення
«Puri.h» файлу з визначенням функцій і з включенням «pure_api.c» файлу в
складу проекту. Єдине обмеження, що накладаються API - рекомендації по
постановці точок зупинки після дзвінків Quantify при виконанні програми під
відладчиком. p>
Розглянемо наявні функції API Quantify: p>
QuantifyAddAnnotation. Дозволяє задавати
словесний опис, що супроводжує тестування коду. Інформація, визначена
розробником цією функцією може бути вилучено з пункту «details» меню
тестування і доступна в LOG-файлі. На її основі, тестер може згодом
використовувати особливі умови тестування; p>
QuantifyClearData. Очищає все
незбережені дані; p>
QuantifyDisableRecordingData.
Забороняє подальшу запис; p>
QuantifyIsRecordingData. Повертає
значення 1 або 0 в залежності від того чи здійснюється запис властивостей чи ні; p>
QuantifyIsRunning. Повертає
значення 1 або 0 в залежності від того проходить тестованої додаток
виконання в звичайному режимі або під Quantify; p>
QuantifySaveData. Ця функція
дозволяє зберігати поточний стан - робити знімок (snapshot); p>
QuantifySetThreadName. Функція
дозволяє розробникам іменувати потоки в довільному іменному поле. За
замовчуванням Quantify дає імена, на зразок «thread_1», що може не завжди
позитивно позначатися на читання одержуваної інформації; p>
QuantifyStartRecordingData. Починає
запис властивостей. За замовчуванням, ця функція автоматично викликається Quantify
при виконанні; p>
QuantifyStopRecordingData.
Зупиняє запис властивостей. p>
Якщо
модифіковані наше тестове програму так, щоб воно використовувало
переваги інтерфейсу API, то може вийти щось наступне p>
int main (int argc, char * argv []) p>
( p>
int
i; p>
QuantifyAddAnnotation ( "Тестування
проводиться під Quantify з використанням API "); p>
QuantifySetThreadName ( "Основний
потік програми "); p>
for (i = 0; i <12; i ++){ p>
QuantifySaveData (); p>
recursive (); p>
) p>
return
0; p>
) p>
В
лістингу показано як можна використовувати основні функції API для видобування
максимального статистичного набору даних. p>
Малюнки
показують зліпки фрагментів екрана Quantify після закінчення тестування. p>
p>
Рис.
Даний малюнок демонструє вигляд вікна після виконання команди зняття відбитку
або виклику функції API QuantifySaveData () p>
p>
Рис.
Зверніть увагу на поле анотації p>
p>
Рис.
У Quantify відсутні труднощі з російськими літерами p>
Збереження
даних і експорт p>
Традиційні
операції над файлами притаманні і програмі Quantify. Додаткові особливості
полягають в тому, що зберігати дані можна як у вбудованому форматі (qfy),
для кожного окремого запуску, так і в текстовому вигляді, для подальшого
використання в текстових редакторів, або для подальшої обробки скриптовою
мовами типу perl (більш докладно про це дивіться в розділах по ClearCase і
ClearQuest). P>
Quantify
дозволить переносити таблиці через буфер обміну в Microsoft Excel, що відкриває
безмежні можливості по множинного порівнянні запусків, з побудови графіків
і різних форм. Все, що необхідно зробити - це тільки скопіювати дані з
однієї програми і помістити в іншу відомим способом. p>
Список літератури p>
Для підготовки даної роботи були використані
матеріали з сайту http://software-testing.ru
p>