You are on page 1of 36

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ

ВІДОКРЕМЛЕНИЙ СТРУКТУРНИЙ ПІДРОЗДІЛ


«ФАХОВИЙ КОЛЕДЖ ІНЖЕНЕРІЇ ТА УПРАВЛІННЯ НАЦІОНАЛЬНОГО
АВІАЦІЙНОГО УНІВЕРСИТЕТУ»

КУРСОВА РОБОТА
з дисципліни «Системне програмування»
на тему: «Розробка профайлера для процесорів ARC»

Студента 4 курсу 705-КІ групи


Спеціальності Комп’ютерна
інженерія
Подзірея Б.В.
(ПІП)
Керівник Кашкевич С.О.

Оцінка
Дата захисту _

Члени комісії
(підпис)(прізвище та ініціали)

(підпис)(прізвище та ініціали)
_
(підпис)(прізвище та ініціали)

Засвідчую, що у цій курсовій роботі


немає запозичень з праць інших
авторів без відповідних посилань.
Підпис студента(ки) _

Київ 2023

1
Міністерство освіти і науки України
Відокремлений структурний підрозділ «Фаховий коледж інженерії та управління
Національного авіаційного університету»

ЗАТВЕРДЖУЮ
Голова комісії

(підпис) (ПІБ)
« _» 2023 р.

ІНДИВІДУАЛЬНЕ ЗАВДАННЯ
Студента(ки) Подзірея Б.В. спеціальності Комп’ютерна інженерія курсу 4
ТЕМА «Розробка профайлера для процесорів ARC»

Вихідні дані середовище розробки IntelliJ IDEA

Зміст ТЧ до курсової роботи:


Індивідуальне завдання
Вступ
Розділ 1. АНАЛІЗ ПОСТАВЛЕНОЇ ЗАДАЧІ ТА ФОРМУВАННЯ
ВИМОГ
Розділ 2. ПРОГРАМНА РЕАЛІЗАЦІЯ МОДУЛЯ
Розділ 3. ТЕСТУВАННЯ ТА КЕРІВНИЦТВО КОРИСТУВАЧА
Висновки
Список літератури
Додаток А – Текст програми

Дата видачі «__» 2023 р. Керівник _


(підпис)

Завдання отримала
(підпис)

2
Тема: «Розробка профайлера для процесорів ARC»

Календарний план виконання курсової роботи:


№ Назва етапу курсової роботи Термін Примітка
виконання
етапу
1. Отримання завдання на курсову роботу.

2. Огляд технічної літератури за темою роботи.

3. Проектування інтерфейсу та алгоритму.

4. Розробка і програмування.

5. Тестування додатку.

6. Аналіз отриманих результатів з керівником,


написання доповіді та попередній захист
курсової роботи.

7. Коригування роботи за
результатами попереднього захисту.

8. Захист курсової роботи.

Студент Подзірей Богдан Валентинович _


Керівник Кашкевич С.О. _
Дата видачі «__» 2023 р.

3
ЗМІСТ

ВСТУП..............................................................................................................................5
РОЗДІЛ 1. АНАЛІЗ ПОСТАВЛЕНОЇ ЗАДАЧІ ТА ФОРМУВАННЯ ВИМОГ.........6
1.1 Огляд технік профілювання..................................................................................6
1.2 Профілювання з використанням вбудованих засобів.........................................8
1.3 Вимоги до створення програмної реалізації......................................................10
РОЗДІЛ 2. ПРОГРАМНА РЕАЛІЗАЦІЯ СИСТЕМИ.................................................11
2.1 Створення UML діаграм додатку.......................................................................11
2.2 Розрахування метрик...........................................................................................14
2.3 Граф викликів.......................................................................................................15
2.4 Обробка результатів.............................................................................................17
РОЗДІЛ 3. ТЕСТУВАННЯ ТА КЕРІВНИЦТВО КОРИСТУВАЧА.........................19
3.1 Тестування програмної реалізації покращення якості.....................................19
3.2 Інструкція користувача........................................................................................20
ВИСНОВОК...................................................................................................................22
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ.....................................................................23
ДОДАТОК 1...................................................................................................................25

4
ВСТУП

Оптимізація програмного забезпечення часто є одним із необхідних кроків у


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

5
РОЗДІЛ 1. АНАЛІЗ ПОСТАВЛЕНОЇ ЗАДАЧІ ТА ФОРМУВАННЯ ВИМОГ

1.1 Огляд технік профілювання


Один із найпоширеніших методів називається профілюванням на основі
пристрою. Класичним прикладом утиліти, яка реалізує цей підхід, є gprof. Цей
метод полягає у введенні в програмний код додаткових дій, спрямованих на збір
необхідних даних за допомогою того ж процесора, на якому виконується
аналізована програма. Наприклад, щоб виміряти час виконання функції, достатньо
зберегти значення системного лічильника перед його викликом, а потім відняти
його від значення лічильника після виходу.
Незважаючи на уявну простоту, цей підхід має ряд труднощів і недоліків.
По-перше, введення додаткових дій у програмний код є окремою
нетривіальною задачею, автоматичне вирішення якої має або підтримуватися
компілятором, або зводитися до зміни вихідного чи результуючого машинного
коду.
По-друге, такий підхід неминуче призводить до викривлення результатів
вимірювань, оскільки для виконання додаткових дій потрібен певний час. Крім
того, потік виконання на сучасних процесорах має вкрай нетривіальну структуру і
дуже чутливий до будь-яких змін у коді. Тому введення коду відстеження може
неявно змінити час виконання наведених нижче інструкцій. Природним
прикладом є зміна вмісту буфера при зберіганні результатів вимірювань в пам'яті.
По-третє, результати вимірювань мають бути десь записані.
Тут також є кілька варіантів. Перший — записувати вихідні результати для
кожного вимірюваного сегмента, наприклад, у форматі: нижня межа, верхня
межа, час, що минув. Цей варіант найшвидший, але споживає багато пам’яті, що
особливо небажано для вбудованих систем. Другий варіант — статистична
обробка результатів відразу після вимірювання. Наприклад, в кінці вимірювання
часу виконання функції шукається запис, що відповідає цій функції, у певній
асоціативної таблиці, а результат потім додається до існуючого значення. Цей
параметр споживає менше пам’яті, але більше спотворює час виконання. Третій

6
варіант – надсилати результати вимірювань через зовнішній канал. В даному
випадку все сильно залежить від пропускної здатності цього каналу і вартості
передачі даних. Наприклад, використовуючи перший підхід до накопичення
буфера, окремий потік для відправки вмісту цього буфера та досить швидкий
канал, цей підхід можна використовувати для отримання відносно точних
результатів.
Другим поширеним підходом до профілювання є профілювання на основі
вибірки, статистичне профілювання. Одним із прикладів утиліти, яка реалізує цей
підхід, є Intel VTune. Опис реалізації наведено на сторінці. Він полягає в
регулярному зчитуванні значення поточного індикатора інструкції та перевірці,
чи не потрапляє воно в один із заздалегідь визначених діапазонів. Наприклад,
діапазони, що відповідають кожній функції, отримуються перед запуском
програми. Далі створюється гістограма кількості звернень до різних функцій, що
дозволяє приблизно оцінити найдорожчі функції.
Такий підхід також має ряд істотних недоліків. По-перше, читання позиції
поточної інструкції може вплинути на процес керування програмою, поки він
повністю не зупиниться. Особливо це видно під час профілювання коду, часу
виконання на віддаленому чіпі. У цьому випадку спроба прочитати регістр
призводить до дорогого ланцюга повідомлень через канал налагодження і впливає
на внутрішній стан процесора. По-друге, такий підхід не дає жодної інформації
про реалізацію землі в реальному часі. Навпаки, вони просто стверджують, що
кожен сайт, ймовірно, займе відповідну частку загального часу.
Крім того, деякі невеликі, але часто звані функції можна повністю
ігнорувати в цьому підході. Цей випадок повністю продемонстрований у RS
Одера в тесті «Пила».
Далі ми розглянемо інструменти для профілювання на ARC.
Synopsys надає набір інструментів розробки, які можна використовувати для
дослідження та оптимізації продуктивності ваших програм. Кожен з них має свої
переваги, недоліки та сфери використання.

7
xCAM є інструментом для створення точних програмних моделей
процесора логічного вентиля. Основною перевагою таких моделей є можливість з
абсолютною точністю аналізувати продуктивність по відношенню до циклів
процесора. Однак швидкість виконання моделі на кілька порядків нижча за
тактову частоту готового процесора, що створює додаткові незручності для
користувача під час профілювання. Крім того, взаємодія програми з іншими
компонентами системи вимагає створення їх моделі.
nSIM є програмним симулятором процесора на рівні команд. Він має значно
вищу швидкість роботи в порівнянні з xCAM, але не гарантує точного
моделювання щодо кількості циклів, витрачених на виконання інструкцій. Його
можна використовувати для пошуку дорогих областей у програмі, але він не
підходить для отримання точних результатів. Крім того, як і у випадку з xCAM,
взаємодія з пристроями сторонніх розробників вимагає створення компонентів,
які її імітують.
RTT (Tracing в реальному часі) — це здатність процесора генерувати велику
кількість інформації про процес.
грам в режимі реального часу за допомогою спеціального модуля,
включеного в схему. Цей інструмент не дозволяє безпосередньо отримувати
інформацію про цикли, які процесор витратив на виконання певного розділу, але
повідомляє про різні події, такі як читання з пам’яті чи запис у пам’ять, взаємодія
з регістрами, а також надає повне відстеження інструкцій. . Інструмент поєднує в
собі переваги відсутності втручання в хід програми та виконує аналіз на повній
швидкості процесора. Основним недоліком є необхідність використання
спеціального обладнання з швидким каналом зв'язку, що дозволяє отримувати
великий обсяг інформації. Таке обладнання досить дороге і може бути недоступне
користувачам.

1.2 Профілювання з використанням вбудованих засобів


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

8
Перший полягає у вимірюванні продуктивності шляхом додавання
додаткових модулів моніторингу до схеми процесора, які підключені до
необхідних компонентів, і завантаження отриманої схеми процесора разом з
іншими модулями в програмовану логічну інтегральну схему (профілювання на
основі FPGA). Наприклад, ви можете додати лічильники, які вмикаються, коли
інструкція виконується за однією адресою, і вимикаються, коли інструкція
виконується за іншою, вимірюючи час виконання цього розділу в цілому та
отримуючи щось подібне до профілювання за допомогою інструментів коду. , але
без додаткових втрат продуктивності та змін програми.
Основною перевагою такого підходу є відсутність будь-якого втручання в
процес виконання коду і, таким чином, забезпечення максимально точних
результатів вимірювань, а також можливість включення.
Це може бути як завгодно складним і функціональним розширенням
(звісно, до обмеження обсягу FPGA). Найбільш істотним недоліком є необхідність
розробки окремого вимірювального модуля для кожної моделі процесора, а в
деяких випадках і для кожної програми, що розглядається. Крім того, такий підхід
не дає можливості оцінити продуктивність на реальному процесорі.
Другий — використання заздалегідь розроблених і жорстко
запрограмованих лічильників різних подій, що відбуваються в процесорі
(комп'ютери продуктивності, лічильники потужності), починаючи з виконання
однієї інструкції і закінчуючи подіями високого рівня, такими як збій буфера
(Профілювання на основі апаратного лічильника). Такий підхід поєднує точність
вимірювання з можливістю коригування лічильників під час роботи для кожної
перевіреної програми та цікавих подій. Крім того, такі лічильники часто містяться
у вже використовуваному готовому процесорі, що дозволяє вивчати поведінку
програми в реальних умовах. Основним недоліком використання лічильників є,
по-перше, їх обмежена кількість і, по-друге, відносно погана конфігураційність
порівняно з перепрограмуванням FPGA або введенням коду вимірювання в
досліджувану програму.

9
1.3 Вимоги до створення програмної реалізації
Метою даної роботи є впровадження профайлера, що дозволяє виконувати
аналіз продуктивності при виконанні програм на процесорах ARC.
Головні ролі:
• вивчити існуючі методи розвитку викладачів та розглянути інструменти
вимірювання продуктивності, що надаються процесорами ARC;
• сформулювати необхідні для користувача сценарії профілювання та
розробити алгоритми їх реалізації;
• розробити та впровадити систему, яка забезпечує можливість виконання
заданих сценаріїв;
• перевірити отриманий розчин і оцінити точність вимірювань.
Для забезпечення нормальної роботи програми повинна бути використана
наступна конфігурація комп’ютера:
‒ мінімальний центральний процесор класу Intel Core i3-4100U 1,8 ГГц;
обсяг оперативної пам’яті не менше 1Гб;
‒ стандартний маніпулятор «миша»;
‒ стандартний SVGA монітор;
‒ операційна система типу Windows, 7, 8, 10.

10
РОЗДІЛ 2. ПРОГРАМНА РЕАЛІЗАЦІЯ СИСТЕМИ

2.1 Створення UML діаграм додатку


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

Рис. 2. 1 – Діаграма класів

Ця підсистема більш детально показана на рисунку 2.2. Вона відповідає за


створення, видалення, моніторинг та виконання різних вимірювань, у тому числі:
• отримання графіка викликів;
• вимірювання кількості подій протягом усього виконання програми.

11
Рис. 2.2 - Структура модуля вимірів

Візуальне моделювання в UML можна представити як процес, що низхідний


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

12
• Сформулювати загальні вимоги до функціональної поведінки
пропонованої системи.
• Розробити вихідну концептуальну модель системи для її подальшого
уточнення у вигляді логічної та фізичної моделей.
• Підготувати вихідну документацію для взаємодії розробників системи з її
замовниками та користувачами.
Суть цієї діаграми полягає в наступному: запропонована система
представляється у вигляді набору сутностей або акторів, які взаємодіють із
системою за допомогою так званих випадків використання. У цьому випадку
актор або актор — це будь-яка сутність, яка взаємодіє з системою ззовні. Це може
бути людина, технічний пристрій, програма або будь-яка інша система, яка може
слугувати джерелом впливу на змодельовану систему, як визначено розробником.
Варіант використання, у свою чергу, використовується для опису послуг, які
система надає суб’єкту. Іншими словами, кожен варіант використання визначає
набір дій, які система виконує в діалозі з актором.
На рисунку 2.3 продемонстровано UML Use Case діаграму програми, що
розробляється.

Рисунок 2.3 – UML Use Case діаграма

Діаграми взаємодії — це сукупність об’єктів, поведінка яких важлива


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

13
Діаграми послідовності іноді називають діаграмами подій відповідно
сценарії подій.
Діаграма послідовності системи управління представлена на рисунку 2.4.

Рисунок 2.4 – Діаграма послідовності системи

2.2 Розрахування метрик


Цей підмодуль відповідає за обробку первинних даних, отриманих за
допомогою лічильників. Його завдання — абстрагувати обчислення всіх
можливих показників від відомих даних. В результаті модуль надає зовнішній
інтерфейс, який отримує набір вимірювань і повертає розраховані показники.
Через потенційно велику кількість можливих показників, для пошуку
доступних використовується наступна схема пошуку. Спочатку створюється і
кешується відображення можливих вихідних даних до показників, які залежать
від них. Наприклад, метрика, яка обчислює відносну кількість циклів для певної
функції, додається до метрик, які залежать від загальної кількості циклів і
локальної кількості циклів. Це створює графік залежностей між вихідними
даними та показниками (приклад на малюнку 2.5).

14
Рис.2.5 - Приклад графіка для обчислення показників.

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

2.3 Граф викликів


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

15
Підрахунок подій у фрагменті коду.
Це вимірювання отримує від користувача необхідний набір вимірюваних
подій і вимагає відповідної кількості лічильників. Крім того, під час ініціалізації
може бути використаний один із двох методів активації лічильників на місці:
контрольно-вимірювальний або діапазон. Після завершення вимірювання
значення використаних лічильників додаються до результатів вимірювань з
позначкою про належність до відповідної області. Одночасно протягом усього
вимірювання
Profiler не заважає виконанню програми. Таким чином, це вимірювання
реалізує сценарій автоматичного вимірювання подій у розділі коду.
Цей тип вимірювань вимагає інформації з графіка виклику, яку можна
отримати за допомогою інших відповідних вимірювань раніше або завантажити
ззовні. Користувач також повинен вказати метрику, за якою буде обчислюватися
батьківський рейтинг дитини, мінімальне значення метрики, точку, в якій
виконується реконфігурація, і функцію, з якої починається аналіз. За
замовчуванням аналіз починається з усіх функцій, які не викликаються іншими,
оскільки вони потенційно можуть бути точками входу в програму.
Аналіз проводиться наступним чином.
Спочатку графік викликів масштабується, а функції додаються до черги в
тому ж порядку. Цей порядок важливий, оскільки він мінімізує помилку при
вимірюванні функцій верхнього рівня, яка виникає внаслідок додаткових витрат
на функції нижнього рівня. Крім того, це дозволяє розрахувати оцінку з часом і
відкинути явно нерелевантні функції. Це означає, що на той час, коли функція
витягується з черги, функції, які її викликають, ймовірно, вже були
проаналізовані. Слід зазначити, що можна гарантувати обчислення оцінки для
всіх функцій, які мають предків, але це недоцільно, тому що в цьому випадку,
навіть якщо лічильники є, доведеться дочекатися завершення аналізу предків.
перш ніж продовжити аналіз самої функції, а деякі лічильники залишаються
неактивними. Запропонований підхід дозволяє, хоча і не ідеально, розділити

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

2.4 Обробка результатів


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

17
По-третє, результати можна представити у вигляді анотованого графіка
викликів DOT. У цьому випадку обчислюються метрики і їх результат
записується разом із вихідними даними як додатковий текст до вузла на графіку.
Крім того, графік можна візуалізувати за допомогою graphviz або будь-якої
подібної програми.

18
РОЗДІЛ 3. ТЕСТУВАННЯ ТА КЕРІВНИЦТВО КОРИСТУВАЧА

3.1 Тестування програмної реалізації покращення якості


В якості основного підходу до тестування коду розробленого рішення ми
обрали написання автоматичних тестів для всіх підсистем із збереженням
залежності від підсистем нижнього рівня. Наприклад, підсистема взаємодії
пристрою перевіряється в інтеграції з реальним налагоджувачем, який запускає
програму на симуляторі процесора ARC. Команди користувача, у свою чергу,
перевіряються за умови, що підсистема взаємодії з пристроєм правильна, а також
повністю інтегрована з налагоджувачем і симулятором.
Бібліотека unittest використовується для прямого написання тестів. Однак
існує проблема з прямим запуском тестів, оскільки вони вимагають попередньої
конфігурації налагоджувача та завантаження в нього модуля профайлера. Тому
для виконання тестів обрано наступний підхід.
Спочатку запускається скрипт для запуску налагоджувача та надання йому
команд для завантаження тестів. Наступний тестовий запуск починається
всередині інтерпретатора Python, вбудованого в LLDB. Технічно це реалізується
як додавання та запуск окремого інтерпретатора команд LLDB. Тести шукаються
та завантажуються в коді обробника цієї команди за допомогою вбудованих
інструментів unittest.
В результаті тести відбуваються всередині налагоджувача і мають повний
доступ до його інтерфейсу.
Рішення було перевірено на тесті для вбудованих систем Embench. Також
проаналізувано невелику програму з використанням бібліотеки zlib. Усі приклади
були запущені на одній із стандартних конфігурацій ядра ARC HS.
При цьому необхідно було оцінити точність отриманого рішення та внесені
додаткові витрати, оскільки ці властивості є найважливішими при оцінці якості
профайлера. Крім того, необхідно було вивчити межі застосування профайлера,
оскільки обраний за основу метод приладобудування вимагає особливих умов.

19
Після виконання тестування можна стверджувати, що програма виконується
коректно.

3.2 Інструкція користувача


Системні вимоги:
1. Операційна система: Windows 8,10.
2. Оперативна пам'ять: щонайменше 1ГБ.
3. Наявність мережі Інтернет.
Ця підсистема надає інтерфейс користувача для більшості можливостей
профайлера у вигляді команд для інтерпретатора LLDB.
Кожна команда представлена у вигляді класу, який імпортується в LLDB,
коли робиться спроба імпортувати модуль профайлера. Кожен клас реєструється
як окрема команда для вбудованого інтерпретатора LLDB і надає посилання на
код обробника команди. Потім команди доступні користувачеві безпосередньо з
LLDB.
Команди логічно розділені відповідно до поділу решти профайлера на
підсистеми. Зокрема, існують набори команд, що відображають взаємодію
підсистеми з пристроєм, автоматичні аналіз і сценарії обробки результатів.
Реалізація команд не несе ніякої додаткової логіки і містить лише виклики
існуючих інтерфейсів інших модулів.
Для оцінки точності було використано моделювання xCAM для
вимірювання кількості циклів, необхідних для виконання функції з ідеальною
точністю. Крім того, результати для тих самих функцій були отримані за
допомогою реалізованого профайлера. Потім було розраховано відхилення
отриманих результатів від ідеалу. Результати наведені в таблиці 1.
Інтервал Додаткові витрати Інтервал
Кількість абсолютні Кількість відносні
Тест Тривалість тесту за виклик
функцій у помилки в викликів помилки за
(циклами) (у петлях)
тесті в тесті
функціях (в функцією(%)
циклах)
aha_mont64 34734 5 13-859 21 40.9 0.2-2.47
crc32 194253 2 14-81 2 40.5 0.01-0.04
cubic 201084 1 18-18 1 18.0 0.01-0.01
edn 553189 9 4-253 9 28.1 0.0-0.84
huffbench 907810 3 818-3277 96 34.1 0.35-3.39
matmult_int 1253556 4 61-7412 803 9.2 0.0-5.81
minver 23416 3 27-379 11 34.5 0.33-2.57

20
nettle_aes 138350 10 12-659 12 54.9 0.02-1.76
nettle_sha256 14077 7 13-499 9 55.4 0.34-12.05
slre 92523 13 77-45949 1168 39.3 13.44-65.09
st 701853 9 9-29018 613 47.3 0.05-6.48
statemate 4403 4 110-239 5 47.8 2.86-5.43
ud 17212 2 19-70 2 35.0 0.15-0.41
zlib 234445 28 1-298 28 10.6 0.0-4.83

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


значенням зі значною похибкою, що не перевищує 5% (у середньому -3%).
Додаткові витрати на кожен виклик майже постійні і порівняні з викликом однієї
порожньої функції. Найгірші результати з’являються з великою кількістю
невеликих функцій, які часто викликаються (наприклад, у тесті slre). У цьому
випадку відсоток помилок накопичується за рахунок додаткових витрат.

21
ВИСНОВОК

У ході курсової роботи було досягнуто наступних результатів:


• Були вивчені існуючі методи розробки профайлера та обрані лічильники
потужності, вбудовані в ядро ARC, як засіб вимірювання продуктивності.
• Розглянуто такі сценарії використання сценаріїв: ручне та автоматичне
вимірювання частини коду, пошук найдорожчих функцій. Запропоновано
алгоритми їх реалізації та оптимізації.
• Розроблено архітектуру профайлера та реалізовано всі розглянуті сценарії.
• Виконання перевірено. Точність оцінювалася на прикладах zlib і Embench,
похибка в циклах вимірювань становила в середньому 3%, але вона значно
зростає при аналізі коротких функцій..

22
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ

1. Alex, N. Roadmap до E-Factory / Alex N., Jr Beavers, Jr. Alex N. Beavers.


– Москва: Машинобудування, 2018. – 747 c.
2. Barabasi, A.L. Linked: the new science of networks / Barabasi A.L.. – М.:
[не зазначено], 2002. – 459 з.
3. Bigus, J.P. Data mining with neural networks / Bigus J.P.. – М.: [не
зазначено], 1996. – 455 с.
4. Ceruzzi, P.E. A history of modern computing / Ceruzzi P.E.. – М.: [не
зазначено], 2003. – 366 с.
5. Danny, Goodman Danny Goodman Applescript Handbook / Danny
Goodman. – Москва: Вища школа, 2003. – 612 с.
6. Darel, W. Hardy Applied Algebra / Darel W. Hardy, Carol L. Walker. –
Москва: Гостехіздат, 2005. – 568 c.
7. Debra, S. Herrmann За допомогою Загальної політики для IT Security
Evaluation / Debra S. Herrmann. – Москва: Вогні, 1983. – 777 c.
8. Gary, V. Vaughan GNU Autoconf, Automake, і Libtool / Gary V. Vaughan
та ін. – Москва: Наука, 1989. – 361 с.
9. Greg, J. Arthaud Systems Analysis in Forest Resources: Proceedings of
Eighth Symposium, Held September 27-30, 2000, Snowmass Village, Colorado, US.A.
(Managing Forest Ecosystems, V. 7) / Greg J. Arthaud, Tara M. Barrett, SYMPOSIUM
ON SYSTEMS ANALYSIS IN FOREST. – Москва: Машинобудування,
1980. – 860 c.
10. Guo, Y. High Performance Data Mining: Scaling Algorithms, Applications
and Systems / Guo Y., Grossman R. (eds.). – М.: [не зазначено], 2002. – 837 c.
11. Karny, M. Probabilistic Advisory System / Karny M., Bohm J., Guy T.V..
– М.: [не вказано], 2003. – 904 с.
12. O., G. Kakde Algorithms для Compiler Design (Electrical and Computer
Engineering Series) / O. G. Kakde. – Москва: ІЛ, 1979. – 465 c.

23
13. Sarker, R.A. Heuristic and optimization for knowledge discovery / Sarker
R.A., Abbass H.A., Newton C.S.. – М.: [не вказано], 2002. – 129 с.
14. Scheutz, M. Computationalism. New directions / Scheutz M.. – М.: [не
вказано], 2002. – 954 c.
15. Weiss, G. Multiagent Systems: A Modern Approach to Distributed Modern
Approach to Artificial Intelligence / Weiss G. – М.: [не зазначено], 1999. – 946 c.
16. Wendy, A. Kellogg New Agenda for Human-Computer Interaction
(Human- Computer Interaction, Vol 15, Nos. 2 & 3) / Wendy A. Kellogg, Clayton
Lewis, Peter Polson. – Москва: Вогні, 2006. – 833 c.
17. Ендрю Троелсен Мова програмування C# 2010 та платформа.net 4.0 5е
видання. 2011. – 1392с.
18. Розміщено Піменов В.Ю. Обчислювально-ефективний метод пошуку
нечітких дублікатів у колекції зображень. 2009-19с.
19. Herbert Bay, Andreas Ess, Tinne Tuytelaars, Luc Van Gool Speeded-Up
Robust Features (SURF) 2008 - 14с
20. Лабор В.В. Сі Шарп: Створення програм для Windows - Мі.: Харвест,
2003. - 384с.
21. Фролов А.В., Фролов Г.В. Мова С# Самовчитель, 2003. - 557с.

24
ДОДАТОК 1

@RestController
@RequestMapping("/api/mirf")
public class MirfController {

Logger log = LoggerFactory.getLogger(MirfController.class);

@Autowired
PipelineExecutor pipelineExecutor;

@Autowired
FileService fileService;

@Autowired
PipelineJobRepository pipelineJobRepository;

@GetMapping("triggerPipeline")
public void triggerPipeline() throws Exception {
pipelineExecutor.triggerPipeline();
}

@PostMapping("mirfSuccess")
public void getResultFromPipeline(@RequestParam("sessionId") String
sessionId,
@RequestParam("file") MultipartFile resultZipFile)
throws IOException, ClassNotFoundException {
InputStream zipInputStream = resultZipFile.getInputStream();
byte[] zipInBytes = zipInputStream.readAllBytes();

25
log.info("Received result from MIRF for session: {}, resultSize: {}",
sessionId, resultZipFile.getSize());

pipelineJobRepository.findAll().forEach(it ->
System.out.println(it.getMirfSessionid()));
PipelineJob relatedPipelineJob =
pipelineJobRepository.findByMirfSessionid(sessionId).get(0);
try {
byte[] resultFileInBytes = MirfZipUtils.unzipResultArchive(zipInBytes);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("-dd-MM-
yyyy-HH-mm-ss");
String startedTime =
formatter.format(relatedPipelineJob.getStartedTime());
FileObject outputFile = fileService.saveFile("pipeline-result" +
startedTime + ".pdf", resultFileInBytes, relatedPipelineJob.getCreator().getId());
relatedPipelineJob.setOutputFile(outputFile);
relatedPipelineJob.setEndTime(LocalDateTime.now());

relatedPipelineJob.setExecutionStatus(PipelineJobStatus.COMPLETED_OK);
} catch (Exception ex) {

relatedPipelineJob.setExecutionStatus(PipelineJobStatus.COMPLETED_ERROR);
log.info("Unable to save result from MIRF, sessionId: {}, reason:{}",
sessionId, ex.getMessage());
}

pipelineJobRepository.save(relatedPipelineJob);
}

@PostMapping("mirfError")

26
public void getErrorFromPipeline(@RequestParam("sessionId") String
sessionId,
@RequestParam("reason") String failReason) {
log.info("Received error from MIRF for session: {}, reason: {}", sessionId,
failReason);

PipelineJob relatedPipelineJob =
pipelineJobRepository.findByMirfSessionid(sessionId).get(0);

relatedPipelineJob.setExecutionStatus(PipelineJobStatus.COMPLETED_ERROR);
pipelineJobRepository.save(relatedPipelineJob);
}
}
@Service
public class MirfOrchestratorClient {

@Value("${mirf.orchestrator.url}")
private String mirfOrchestratorUrl;

@Value("${mirf.orchestrator.port}")
private String mirfOrchestratorPort;

@Value("${mirf.orchestrator.url.pipeline.start}")
private String mirfPipelineEndPoint;

@Value("${mirf.orchestrator.url.sessionId}")
private String mirfSessionIdEndpoint;

@Value("${mirf.orchestrator.url.registerClient}")

27
private String mirfRegisterClientEndpoint;

public final static String DEFAULT_PIPELINE = "[\n" +


" { \"id\":
0, \"blockType\" : \"DicomImageSeriesReaderAlg\", \"children\": [1, 3] },\n" +
" { \"id\": 1, \"blockType\" : \"DicomAddCircleMaskAlg\", \"children\":
[2] },\n" +
" { \"id\":
2, \"blockType\" : \"ConvertHighlightedDicomImagesToPdfAlg\", \"children\": [4] },\n"
+
" { \"id\":
3, \"blockType\" : \"ConvertDicomImagesToPdfAlg\", \"children\": [4] },\n" +
" { \"id\": 4, \"blockType\" : \"PdfFileCreatorAlg\", \"children\": [] }\n" +
"]\n";

public final static String DEFAULT_PIPELINE2 = "[\n" +


" { \"id\": 0, \"blockType\" : \"PipelineForDeveloping\", \"children\": [] }\
n" +
"]\n";

public final static String IHD_PIPELINE = "[\n" +


" { \"id\": 0, \"blockType\" : \"IhdDataReaderAlg\", \"children\": [] }\n" +
"]\n";

private CloseableHttpClient httpclient = HttpClients.createDefault();

public Boolean processPipeline(String sessionId, String pipelineConfiguration)


throws IOException, URISyntaxException {

28
URI repositoryUri = new URI("http", null, mirfOrchestratorUrl,
Integer.parseInt(mirfOrchestratorPort), null, null, null);

HttpPost post = new HttpPost(repositoryUri.toString() +


mirfPipelineEndPoint);

MultipartEntityBuilder builder = MultipartEntityBuilder.create();

builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addTextBody("sessionId", sessionId,
ContentType.DEFAULT_BINARY);
builder.addTextBody("pipeline", pipelineConfiguration,
ContentType.DEFAULT_BINARY);

HttpEntity entity = builder.build();


post.setEntity(entity);

HttpResponse response = httpclient.execute(post);

return response.getStatusLine().getStatusCode() == 200;


}

public String getSessionId() throws Exception {

URI repositoryUri = new URI("http", null, mirfOrchestratorUrl,


Integer.parseInt(mirfOrchestratorPort), null, null, null);

HttpGet request = new HttpGet(repositoryUri + mirfSessionIdEndpoint);

try (CloseableHttpClient httpClient = HttpClients.createDefault();

29
CloseableHttpResponse response = httpClient.execute(request)) {

HttpEntity entity = response.getEntity();


if (response.getStatusLine().getStatusCode() == 200 && entity != null) {
String sessionId = EntityUtils.toString(entity);
return sessionId;
} else {
throw new Exception("Mirf service unavailable");
}
}
}
}
@Service
public class MirfRepositoryClient {

@Value("${mirf.repository.url}")
private String mirfRepositoryUrl;

@Value("${mirf.repository.port}")
private String mirfRepositoryPort;

@Value("${mirf.repository.url.archive}")
private String mirfRepositoryArchiveEndPoint;

private CloseableHttpClient httpclient = HttpClients.createDefault();

/**
* @param filename name of archive, it should be in *.zip format
*/
public Boolean sendArchive(String sessionId, String filename, byte[]

30
zipArchive) throws IOException, URISyntaxException, InterruptedException {

URI repositoryUri = new URI("http", null, mirfRepositoryUrl,


Integer.parseInt(mirfRepositoryPort), null, null, null);

HttpPost post = new HttpPost(repositoryUri.toString() +


mirfRepositoryArchiveEndPoint);

MultipartEntityBuilder builder = MultipartEntityBuilder.create();

builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addTextBody("sessionId", sessionId,
ContentType.DEFAULT_BINARY);
builder.addBinaryBody("file", zipArchive,
ContentType.DEFAULT_BINARY, filename);

HttpEntity entity = builder.build();


post.setEntity(entity);

HttpResponse response = httpclient.execute(post);

return response.getStatusLine().getStatusCode() == 200;


}

} @Service
@Lazy
public class OrthancInstancesClient {

@Value("${orthanc.url.instances}")
private String orthancInstancesUrl;

31
@Value("${orthanc.credentials.username}")
private String orthancUsername;

@Value("${orthanc.credentials.password}")
private String orthancPassword;

private CredentialsProvider credentialsProvider;

@PostConstruct
public void init() throws IOException {
credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(orthancUsername,
orthancPassword)
);
}

public void getAllAvailableInstanceIds() throws IOException {

HttpGet request = new HttpGet(orthancInstancesUrl);

try (CloseableHttpClient httpClient = HttpClientBuilder.create()


.setDefaultCredentialsProvider(credentialsProvider)
.build();
CloseableHttpResponse response = httpClient.execute(request)) {

HttpEntity entity = response.getEntity();


if (entity != null) {

32
String jsonResponse = EntityUtils.toString(entity);
}
}
}

public String uploadInstance(byte[] dicomFile) throws IOException {


HttpPost request = new HttpPost(orthancInstancesUrl);
ByteArrayEntity requestEntity = new ByteArrayEntity(dicomFile);

request.setEntity(requestEntity);
request.setHeader("Accept", "application/dicom");
request.setHeader("Content-type", "application/dicom");

try (CloseableHttpClient httpClient = HttpClientBuilder.create()


.setDefaultCredentialsProvider(credentialsProvider)
.build();
CloseableHttpResponse response = httpClient.execute(request)) {

HttpEntity responseEntity = response.getEntity();


if (responseEntity != null) {
String json = EntityUtils.toString(responseEntity);
return getIdFromResponse(json);
} else {
throw new RuntimeException("Pacs server response is empty");
}
}
}

public byte[] downloadInstance(String instanceId) throws IOException {


HttpGet request = new HttpGet(orthancInstancesUrl + "/" + instanceId +

33
"/file");

request.setHeader("Accept", "application/dicom");

try (CloseableHttpClient httpClient = HttpClientBuilder.create()


.setDefaultCredentialsProvider(credentialsProvider)
.build();
CloseableHttpResponse response = httpClient.execute(request)) {

HttpEntity responseEntity = response.getEntity();


if (responseEntity != null) {
InputStream is = responseEntity.getContent();
return IOUtils.toByteArray(is);
} else {
throw new RuntimeException("Pacs server response is empty");
}
}
}

public ByteArrayInputStream previewInstance(String instanceId) throws


IOException {
HttpGet request = new HttpGet(orthancInstancesUrl + "/" + instanceId +
"/rendered");

request.setHeader("Accept", "image/jpeg");

try (CloseableHttpClient httpClient = HttpClientBuilder.create()


.setDefaultCredentialsProvider(credentialsProvider)
.build();
CloseableHttpResponse response = httpClient.execute(request)) {

34
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream is = entity.getContent();
byte[] bytes = IOUtils.toByteArray(is);
return new ByteArrayInputStream(bytes);
}
}
return null;
}

public void deleteInstance(String instanceId) throws IOException {


HttpDelete request = new HttpDelete(orthancInstancesUrl + "/" +
instanceId);

try (CloseableHttpClient httpClient = HttpClientBuilder.create()


.setDefaultCredentialsProvider(credentialsProvider)
.build();
CloseableHttpResponse response = httpClient.execute(request)) {

HttpEntity entity = response.getEntity();


if (entity != null) {
String result = EntityUtils.toString(entity);
}
}
}

private String getIdFromResponse(String json) throws


JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();

35
Map<String, Object> resultMap = objectMapper.readValue(json, new
TypeReference<Map<String,Object>>(){});
return resultMap.get("ID").toString();
}
}

36

You might also like