Аtomic-тести і файнтюнинг продуктивності

imageБудь-який програмний продукт складніше «Hello, world!» необхідно тестувати — це аксіома розробки. І чим ширше його функціональність і складніше архітектура, тим більше уваги потрібно приділяти тестування. Особливо обережно треба ставитися до гранулярному вимірювання продуктивності. Часто буває, що в одній частині прискорили, а в іншій забарилися, в підсумку результат нульовий. Щоб такого не відбувалося, ми у своїй роботі дуже активно використовуємо так звані atomic-тести. Що це таке і з чим їх їдять, читайте під катом.

Історія
imageУ міру розвитку Parallels Desktop нам ставало все важче тестувати окремі функціональності, а також знаходити після чергових оновлень і оптимізацій причини погіршення продуктивності віртуальної машини. Через складності системи цілком покрити її unit-тестами було практично нереально. А стандартні пакети вимірювання продуктивності працюють з невідомих для нас алгоритмами. До того ж вони заточені під вимірювання не віртуальних, а реальних середовищах, тобто стандартні бенчмарки менш «чутливі» до внесеним змінам.

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

Atomic-тести
В результаті, ми розробили набір вузькоспеціалізованих — атомарних, atomic — тестів. Кожен з них призначений для вимірювання продуктивності якоїсь типової механіки гіпервізора (наприклад, обробка VM-exit'ів) або операційної системи (скидання TLB-кешей з TLB-shootdown, перемикання контекстів тощо). Також з допомогою atomic-тестів ми оцінюємо виконання найпростіших операцій, що не залежать від реалізації операційної системи і віртуальної машини: математичні обчислення, роботу з пам'яттю і диском в різних початкових умовах, і так далі.



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





Розв'язувані задачі
Звичайно, тестування функціональності саме по собі — працює або не працює, і якщо працює, то коректно? — річ важлива і необхідна. Але цим не обмежується коло завдань, розв'язуваних нами з допомогою atomic-тестів.

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



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

Друга задача, розв'язувана з допомогою atomic-тестів — порівняння з конкурентами. Беремо дві системи і тестуємо на одній машині під різними гіпервізорами. Якщо наш продукт в якійсь області поступається в продуктивності, то розробники починають вдумливо розбиратися, чому так.

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

Особливості використання atomic-тестів
Atomic-тести можуть виконуватися де завгодно — на хост або гостьової ОС. Але, як і будь-тест продуктивності, вони залежать від конфігурації операційної системи та обладнання. Так що, якщо запустити тести на хостовой ОС, яка не збігається з гостьовою, то отримані результати будуть марні.

Як і будь-тестів продуктивності, їм потрібні певні умови, щоб виходили відтворювані результати. Хостова ОС дуже складна (завдяки системі віртуалізації) і не є операційною системою реального часу. В ній можуть з'являтися непередбачувані затримки, пов'язані з обладнанням, активізуватися різні сервіси. Гіпервізор — теж складний програмний продукт, що складається з численних компонентів, які працюють в просторі користувача, просторі ядра і власному контексті. Гостьова ОС схильна тим же проблемам, що і хостова. Так що найскладніше при використанні atomic-тестів — отримати повторюваність результатів тестування.

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

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

Якщо умови проведення вимірювань змінилися, то їм шукається якесь розумне пояснення, і завжди порівнюються результати до і після змін.

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

1) спочатку вивести потік віртуального процесора зі стану спокою за допомогою сигналу від хостової ОС
2) перейти з його контексту у власний контекст гіпервізора
3) передати переривання в гостьову ОС

Проблема в тому, що процеси перемикання контексту гіпервізора в контекст ядра і назад дуже повільні. І коли віртуальний процесор знаходиться в стані спокою (idle), при поверненні йому управління виходять дуже високі витрати.

Одного разу в Parallels Desktop ми зіткнулися з дефектом в MacOS X Yosemite 10.10. Система генерувала апаратні переривання з такою інтенсивністю, що ми тільки і робили, що їх обробляли, і, в результаті, гостьова ОС вісла. Ситуація ускладнювалася тим, що надходять в контексті гостьової ОС апаратні переривання потрібно було негайно передати хостової ОС. А для цього доводиться два рази перемикати контекст. І при великій кількості таких переривань гостьова ОС гальмувала або зависала. Саме тут у пригоді наші atomic-тести.

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



Будемо раді відповісти на питання в коментарях до статті. Також питайте, якщо щось здалося незрозумілим.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.