Лекция 9 - Качество кода. Оптимизация

advertisement
СОВРЕМЕННЫЕ
ТЕХНОЛОГИИ
РАЗРАБОТКИ ПО
Лекция 9:
Качество кода:
Профилирование и оптимизация
Мотивация
• Скорость работы – часть
нефункциональных требований
• Недостаточная скорость –
серьёзный ущерб качеству
• вплоть до провала проекта
• Нельзя полагаться, что завтра
компьютеры станут мощнее
2
Что делать?
• «Преждевременная оптимизация»
• не стоит ею заниматься – пока неизвестно,
где «узкое место» (bottleneck), может
оказаться пустой тратой времени
• но выбор алгоритмов и структур данных
полезно продумывать заранее
• Мониторинг эффективности
• профилирование и контроль за расходом
памяти
• Оптимизация «узких мест»
3
Профилирование
• Сбор статистики: производительность кода
программы
• Профилировщик – отладчик особого рода,
контролирует выполнение, собирает данные
и формирует отчёт:
•
•
•
•
•
Общее время исполнения функции/метода
Удельное время исполнения
Количество вызовов
Структура вызовов функций (caller/callee)
Места возникновения конфликтов и пенальти
4
Профилировщики
• Способы сбора информации:
• Event-based (вызовы, возвраты,
исключения
• Sampling (сбор статистики)
• Instrumentation (модификация кода
приложения)
5
Профилировщики: примеры
•
•
•
•
•
•
•
MSVS: штатный профилировщик
Intel VTune
AMD CodeAnalyst
Valgrind
xDebug (PHP)
Python – модули cProfile, hotspot
...
6
Профилирование:
тестовые данные
Главные характеристики
• Воспроизводимость сценария
• Репрезентативность
• тест максимально близок к реальному
применению
• Полнота охвата
• может потребоваться несколько сценариев
7
Оптимизация
• Всегда ищем узкое место (bottleneck)
• Прибегаем к профайлерам и логам
• Компромисс между оптимизацией и
параллелизмом
• оптимизации могут усложнять код
• Контролируем результаты
• повторное профилирование
8
Оптимизация:
подходы
В порядке приоритета:
1. Более эффективные алгоритмы и структуры
данных
2. Высокоуровневые средства:
•
•
Распараллеливание
Кэширование/мемоизация, эффективная работа с
памятью
Низкоуровневые средства:
3.
•
•
•
•
Учёт специфики платформ (размеры линии кэша,
размещение инструкций, компиляторная оптимизация,
векторные инструкции, и т.п.)
Библиотечные функции от производителей CPU
Синтаксические трюки конкретных языков
Вычисления на графических картах
9
Оптимизация:
Алгоритмы
Затраты на выполнение растут, потому что:
• Алгоритм избыточно сложен
• Примеры:
- fib(n) = fib(n-2) + fib(n-1)
- pow(x, n) = x * pow(x, n-1)
• Решение – поиск алгоритмов с меньшей
вычислительной сложностью (в O-нотации)
- т.е. O(n logn) в среднем лучше, чем O(n^2)
- но на малых n может быть и наоборот
• Задержки при доступе к памяти
• последовательный доступ лучше случайного
- в т.ч., оптимизируется работа с кэшем процессора
• чем меньше обращений к памяти – тем лучше
10
Оптимизация:
Структуры данных
• Источники проблем:
• Накладные расходы на работу со структурами:
выделение/освобождение памяти
• Сложность алгоритмов доступа к структурам
- время поиска/добавления/удаления элемента, сортировки, и т.п.
• На примере STL:
• std::vector плох при частом добавлении/удалении элементов
(особенно произвольном)
- deque лучше подходит для вставки в голову/хвост
- list – для вставки/удаления в произвольной позицию
• последовательные контейнеры (vector, list, deque) хуже при
частом поиске или поддержании порядка
- лучше set/map, hash_set/hash_map
• и т.п.
11
Оптимизация:
Параллелизм
• Эффективен на многоядерных
архитектурах
• Основные виды:
• Параллелизм заданий
- примеры: фоновые задачи (проверка
орфографии, загрузка файлов из сети, и т.п.)
• Параллелизм данных
- один алгоритм, параллельно обрабатываются
разные блоки данных
12
Оптимизация:
Параллелизм
• Параллелизм заданий:
• Организация выполнения задач
несколькими потоками
• Эффективная организация выполнения:
пулы потоков (thread pools)
• Параллелизм данных:
• Дробление данных на блоки
13
Параллелизм: ограничения
• Синхронизация между потоками
• Накладные расходы на запуск задач
• на малых блоках может быть
неэффективно
• Ложное распределение памяти
• два процессора пишут в одно место кэша –
падает эффективность его использования
• Пропускная способность памяти
14
Оптимизация:
Параллелизм – OpenMP
• OpenMP (Open Multi-Processing)
• промышленный стандарт для компиляторов C++
• набор директив препроцессора (#pragma) для
указания, как организуется параллелизм
• тривиальный пример:
#pragma omp parallel for
for (i = 0; i < N; i++)
a[i] = 2 * i;
• также есть механизм запуска фоновых задач:
#pragma omp parallel sections
{
#pragma omp section
{ /*some job*/ }
#pragma omp section
{ /*some other job*/ }
}
15
Оптимизация
Работа с памятью
• Кэширование/мемоизация:
• хранение заранее подготовленных данных
- для трудоёмких вычислений
- для медленных/дорогих операций
• загрузка из сети/с диска
• использование этих значений из таблицы при
повторном обращении
• Примеры:
• кэш картинок: «плитки» карт (gmaps/OSM),
превью-картинки, и т.п.
• таблица значений sin/cos в интенсивных
вычислениях
• промежуточные результаты при работе с
документами/изображениями (для Undo)
16
Низкоуровневые оптимизации
• Платформо-специфичный код:
• размеры линии кэша, развёртывание циклов, размещение
инструкций, предсказание ветвлений, компиляторная
оптимизация, векторные инструкции, и т.п.
• вплоть до ассемблерного кода
• Библиотечные функции от производителей CPU
• пример: функции discrete cosine transform на DSP
• Синтаксические трюки конкретных языков
• оптимальные способы обмена переменных, работы со
строками, передачи аргументов и т.п.
• Вычисления на графических картах
• GPGPU: CUDA, OpenCL, DirectX
17
Ссылки и литература
• Р. Гербер, А. Бик, К. Смит, К. Тиан,
«Оптимизация ПО. Сборник рецептов»
• С. Мейерс, «Эффективное использование
STL»
• раздел «Контейнеры»:
http://cpp.com.ru/meyers/ch1.html
• Статья «Алгоритм выбора STL-контейнера»
• http://habrahabr.ru/company/infopulse/blog/194726/
• OpenMP: http://en.wikipedia.org/wiki/OpenMP
• GPGPU: CUDA, OpenCL:
• http://www.slideshare.net/AlexTutubalin/ss-5321298
18
Download