Сразу определимся с тем, что же такое "оптимизация" программы. Под оптимизацией будем понимать изменение программного кода с целью сокращения использования тех или иных ресурсов системы за счет его усложнения.
Это правило кажется самым простым в своем применении. Ведь оно поощряет сокращение объема работы. Но такое ощущение обманчиво. Очень часто возникает соблазн выполнить оптимизацию преждевременно. В результате структура программы усложняется. Читать ее становится труднее. Как следствие, код оказывается тяжело сопровождать. Но даже это не самое плохое. Проблема в том, что вы можете потратить кучу времени на оптимизацию не того, что надо.
Вывод: На первом этапе разработки пишите самый простой код (простой, но не примитивный! простой = понятный), который решает поставленную задачу, не задумываясь об ограничении ресурсов.
Предположим, первый этап пройден. Простая версия программы работает правильно, но недостаточно эффективно. И здесь возникает вопрос, а как измерять эффективность? Не бывает абстрактной эффективности. Она всегда определяется относительно чего-то. Например, эффективность относительно расхода оперативной памяти или эффективность относительно использования ресурсов процессора. В связи с этим мы приходим к следующему шагу: выберите критерии оптимизации. В качестве таких критериев можно рассматривать любую значимую характеристику приложения (от размера исполняемого файла до скорости передачи данных по сети).
Ни в коем случае не пытайтесь определить соответствие критериям "на глаз". Все равно ошибетесь. Найдите способ проведения точных (или максимально приближенных) измерений. Для некоторых характеристик это сделать проще (например, размер программы), а для некоторых сложнее (например, максимальное число одновременно обслуживаемых пользователей).
Итак, вы убедились, что программа не соответствует одному или нескольким выбранным критериям. Что теперь? Бежать переписывать все подряд? Не спешите. Если начать менять код сейчас, то вы опять будете работать вслепую, делая множество лишней работы. Нам известно, что в программе где-то есть проблема. Но где она? И вновь нам нужны дополнительные инструменты для измерений. Типичным решением является использование профилировщика. Сам по себе профилировщик - средство, которое умеет подсчитывать количество вызовов, время нахождения и другие полезные цифры для каждой процедуры программы. Реализация профилировщика может быть какой угодно (многое зависит от платформы): от внешнего приложения до библиотеки. Если нашли более подходящий инструмент для своих целей - используйте его.
Прекрасно. Допустим, нам удалось найти то узкое место, которое все тормозит. Вот с ним и нужно работать.
Вывод: Любая оптимизация должна быть осмысленной. Вполне возможно, что она и не требуется. Если не сделать соответствующие измерения, то вы об этом не узнаете.
При просмотре проблемной функции в первую очередь проверьте, используются ли для решения задачи подходящие алгоритмы и структуры данных. Именно это может стать причиной неэффективной работы приложения.
Грубый пример: в функции используется линейный поиск по массиву. Если для массивов небольшого размера это вполне приемлемо, то для больших массивов это становится проблемой. В этом случае можно использовать множество, а не массив, если не требуется упорядоченность элементов. Или же вместо простого линейного поиска можно попробовать бинарный.
Еще один пример: вы используете быструю сортировку, но она работает не достаточно шустро. Проверьте улучшится ли что-то, если поменять ее на сортировку слиянием или вставками. Быстрая сортировка - не всегда оказывается наилучшей.
Замечу, что рассмотренные выше примеры лучше считать не оптимизацией, а исправлением ошибок проектирования.
Если в рассматриваемой функции уже используются наиболее подходящие стандартные алгоритмы и структуры данных, то вам не обойтись без многопоточности. Рекомендую посмотреть хороший пример из другой моей статьи, где мы ускорили алгоритм обработки изображения в 4 раза с помощью QtConcurrent
.
Но и это не полный перечень возможных оптимизаций. Часто удается сократить объем необходимых вычислений, введя дополнительные неочевидные ограничения. Вспомните тот же алгоритм "Решето Эратосфена" для поиска простых чисел. Другая возможная оптимизация заключается в использовании приблизительных расчетов. Если вам достаточно двух знаков после запятой, то нет смысла искать значение до шестого. Еще одна тактика заключается в использовании таблиц констант. Например, для получения значений тригонометрических функций. Думаю, что вы и сами сможете придумать не одну уловку, которая сможет улучшить эффективность вашей программы. Все зависит от контекста.
Вывод: Начните с наиболее очевидных способов оптимизации. Обязательно проведите повторные замеры рабочих характеристик нового кода (после любых изменений), иначе как вы узнаете, что не стало еще хуже, чем было? Если этого оказалось недостаточно, то ориентируйтесь по ситуации. Попробуйте добавить параллельную обработку или приблизительные методы расчетов.
Если оптимизация прошла успешно, то вполне вероятно, что код, над которым вы работали, стал менее понятным. Это может оказаться проблемой в плане дальнейшего сопровождения системы. Поэтому сразу после проведения оптимизации не помешает написать комментарий, в котором будет указано:
Вывод: Если код сложный, то для него требуются пояснения. В будущем эти пояснения могут помочь именно Вам.
Но что делать, если вы уже попробовали все, что можно, но программа все равно не укладывается в критерии оптимальности? Здесь у вас может быть несколько вариантов:
Вывод: Если у задачи нет решения на одном уровне абстракции, то оно может найтись на другом.
Anonymous:
… и все это разобьется об недостаточность понимания заказчиком того, что он хочет. Причем не только в случае заказчика из России.
Да. В реальном мире теория так хорошо не работает, как звучит, а статья несколько идеализирована. Но оговорить с заказчиком хотя бы примерные ограничения в рамках технического задания не помешает. В идеале эти требования затем должны стать основой для формальной части приемочного тестирования.
Anonymous:
Я бы добавил главное: важно делать программу настолько гибкую, насколько это вообще можно. Чтобы костыли были хотя бы красивыми.
Имхо.
Про гибкость в целом тоже согласен. С другой стороны, с этим можно перестараться, как и со всем остальным. Те же паттерны проектирования дают гибкие решения, но сами авторы (банда четырех) советуют не использовать их где попало. Хотя то, что программа должна легко поддаваться изменениям и рефакторингу, - естественно.
Anonymous
… и все это разобьется об недостаточность понимания заказчиком того, что он хочет. Причем не только в случае заказчика из России.
Я бы добавил главное: важно делать программу настолько гибкую, насколько это вообще можно. Чтобы костыли были хотя бы красивыми.
Имхо.