l8_nc
.pdfКонструктивные изменения
•Кэширование или буферизацию, чтобы ускорить доступ к данным или избежать длительных повторяющихся вычислений.
•Создать пул ресурсов для сокращения расходов на выделение памяти объектам.
•Пожертвовать точностью ради скорости, если это допустимо. Изменить формат хранения данных или их представления на диске, сделав его более подходящим для скоростной обработки. Например, ускорить синтаксический анализ текстового файла, воспользовавшись двоичным форматом. Передавать или хранить файлы в сжатом виде, чтобы повысить эффективность их передачи по сети.
•Применить распараллеливание задач и многопоточность, чтобы одно действие не ждало окончания выполнения другого.
•Эффективно организовать потоки: избегать или удалять лишние блокировки.
•Не злоупотреблять исключениями (exceptions). Они могут помешать компилятору выполнить оптимизацию и нанести ущерб быстродействию, если применяются слишком часто.
•Ограничить функциональность: самый быстрый код тот, который вообще не выполняется.
•Пожертвовать качеством конструкции ради скорости. Например, сократить косвенность и увеличить связывание.
© 2013 NetCracker Technology Corporation Confidential |
21 |
Модификация кода
•Развертывание циклов. Если у цикла очень короткое тело, то структура, образующая цикл, может обойтись дороже, чем сама циклическая операция. Избавьтесь от накладных расходов, сделав структуру плоской – замените цикл из 10 итераций на 10 последовательных операторов. Развертывание циклов может быть частичным, что бывает оправдано для больших циклов. Можно выполнить в каждой итерации четыре операции и каждый раз увеличивать счетчик цикла на четыре. Однако такой прием оказывается затруднителен, если количество итераций цикла не кратно количеству развернутых операций.
•Встраивание кода. При выполнении коротких операций накладные расходы на вызов функции могут оказаться чрезмерными. С помощью разбиения кода на функции достигаются значительные преимущества: более понятный код, единообразие в результате многократного использования и возможность модификации в отдельной области. Однако для увеличения быстродействия можно освободиться от функций и объединить вызывающий код с вызываемым. Для этого есть несколько способов. При наличии соответствующей поддержки в языке можно сделать это в исходном коде (в C/C++ с помощью ключевого слова inline), при этом в значительной мере сохраняется читаемость кода. В противном случае придется соединять код самостоятельно, либо многократно копируя функцию, либо делая это с помощью препроцессора. Затруднительно встраивание обращений к рекурсивным функциям – как узнать, когда нужно остановиться? Попытайтесь заменить рекурсию другими алгоритмами. Встраивание часто открывает возможность дальнейшей оптимизации на уровне кода (ранее невозможной внутри границ функции).
•Свертывание констант Вычисления с использованием констант можно проводить во время компиляции, что сокращает объем действий, проводимых на этапе выполнения. Простое выражение типа return 6+4; можно свести к return 10;. В результате упорядочения членов большого выражения можно свести вместе две константы, что позволит сократить выражение. Обычно программисты не пишут таких очевидных вещей, как return 6+4;. Однако такого рода выражения часто встречаются после расширений макросов.
•Перенос действий в этап компиляции. На этапе компиляции можно не только свертывать константы. Можно статически проверять условия и удалять лишнее из кода. От ряда проверок можно вообще избавиться, например убрать проверку отрицательных значений, использовав беззнаковые типы данных.
•Понижение прочности Речь идет о замене операции другой, которая выполняется быстрее. Это особенно эффективно на ЦП со слабой поддержкой арифметики. Например, заменить умножение или деление целых чисел на сдвиг или сложение; x/4 можно преобразовать в x>>2, если эта операция выполняется быстрее на вашем процессоре.
•Вложенные выражения Устранение одинаковых вложенных выражений позволяет избежать повторного вычисления выражений, значения которых не изменились. В коде
int first = (a * b) + 10; int second = (a * b) / c;
выражение (a * b) вычисляется два раза. Достаточно одного. Можно выделить общее выражение и заменить его на
int temp = a * b;
int first = temp + 10; int second = temp / c;
•Удаление «мертвого» кода. Не пишите лишнего кода; уберите все, что не является необходимым для работы программы. Статический анализ покажет функции, которые никогда не вызываются, и участки кода, которые никогда не получают управления. Удалите их.
© 2013 NetCracker Technology Corporation Confidential |
22 |
Зактыки с производительностью Java
© 2013 NetCracker Technology Corporation Confidential |
23 |
Первый шаг
«я вижу, что метод foo() реализован неэффективно»
«по профайлеру видно, что метод bar() – горячий и занимает 5%» «по-моему, у нас тормозит БД, и нужно перейти с DB 1 на DB 2"
•Правильный первый шаг:
•Выбрать метрику
‒ops/sec, transactions/sec
‒время исполнения
‒время отклика
•Убедиться в корректности метрики
‒релевантна (учитывает реальный сценарий работы приложения)
‒повторяема
© 2013 NetCracker Technology Corporation Confidential |
24 |
Метрики
•Throughput, Bandwidth. Количество работы, выполненное за единицу времени:
‒MB/sec
‒ops/sec, transactions/sec
‒FPS frames per second
•Время...
•...работы
‒Execution time: общее время исполнения
•...отклика
‒Latency: время отдельной операции
‒Response time: задержка между стимулом и реакцией
•...запуска
‒Startup time: время до начала работы
‒Time to performance: время до начала хорошей работы
•Память
© 2013 NetCracker Technology Corporation Confidential |
25 |
Что можно улучшить
•Уровень системы
•I/O (Сеть/Диск)
•Операционная система
•Процессор/память
•Уровень JVM
•Издержки работы самой JVM
•Время GC
•Уровень приложения
•Количество потоков (мало или даже много)
•Лишние блокировки, синхронизация
•Алгоритмические проблемы (лишние вызовы, неэффективные структуры данных и алгоритмы)
•Архитектурный уровень
•Кеширование
•Распределение нагрузки на нескольких узлов
•Оптимизация взаимодействия между слоями
© 2013 NetCracker Technology Corporation Confidential |
26 |
Уборка мусора
© 2013 NetCracker Technology Corporation Confidential |
27 |
Уборка Мусора
•Мусор – объект в памяти не достижимый из программного кода
•Уборка бывает:
•Подсчет ссылок
•Транзитивное замыкание ссылок
•Вообще не собирать
© 2013 NetCracker Technology Corporation Confidential |
28 |
Подсчёт ссылок
«+»
•Просто
•Не требует остановки приложения
«-»
•Не очищает циклические структуры
•Дополнительная нагрузка на ЦП
•Плохо сочетается с многопоточностью
© 2013 NetCracker Technology Corporation Confidential |
29 |
Транзитивное замыкание ссылок
«+»
•Есть корневые объекты (локальные и глобальные переменные). Все объекты достижимые – живые, не достижимые из корневых - мусор
«-»
•Граф ссылок не должен меняться при обходе -> нужно тормозить предложение (stop- of-word)
© 2013 NetCracker Technology Corporation Confidential |
30 |