Мои Конспекты
Главная | Обратная связь


Автомобили
Астрономия
Биология
География
Дом и сад
Другие языки
Другое
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Металлургия
Механика
Образование
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Туризм
Физика
Философия
Финансы
Химия
Черчение
Экология
Экономика
Электроника

Соблюдайте осторожность при оптимизации



 

Есть три афоризма, посвященных оптимизации, которые обязан знать каждый.

Возможно, они пострадали от слишком частого цитирования, однако при ведем их на тот случай, если вы с ними не знакомы:

Во имя эффективности (без обязательности ее достижения) делается больше вычислительных ошибок, чем по каким-либо иным причинам, включая непроходимую тупость.

- Уильям Вульф (William А. Wulf) [Wulf72]

Мы обязаны забывать о мелких усовершенствованиях, скажем, на 97 %

рабочего времени: опрометчивая оптимизация - корень всех зол.

- Дональд Кнут (Donald Е. Knuth) [Knuth74]

 

Что касается оптимизации, то мы следуем двум правилам:

Правило 1. Не делайте этого.

Правило 2 (только для экспертов). Пока не делайте этого - т. е. нетпока у вас абсолютно четкого, но неоптимизированного решения.

- М. А. Джексон (М. А. Jackson) {fackson75]

 

Эти афоризмы на два десятилетия опередили язык программирования Java. В них отражена сущая правда об оптимизации: легче причинить вред, чем благо, особенно если вы взялись за оптимизацию преждевременно. В процессе оптимизации вы можете получить программный код, который не будет ни быстрым, ни правильным, и его уже так легко не исправить.

 

 

 

 

Не жертвуйте здравыми архитектурными принципами во имя производительности. Старайтесь писать хорошие программы, а не быстрые. Если хорошая программа работает недостаточно быстро, ее архитектура позволит осуществить оптимизацию. Хорошие программы воплощают принцип сокрытия информации (information hiding): по возможности они локализуют конструкторские решения в отдельных модулях, а потому отдельные решения можно менять, не затрагивая остальные части системы (статья 12).

Это не означает, что вы должны игнорировать вопрос производительности до тех пор, пока ваша программа не будет завершена. Проблемы реализации могут быть решены путем последующей оптимизации, однако глубинные архитектурные пороки, которые ограничивают производительность, практически невозможно устранить, не пере писав заново систему. Изменение задним числом фундаментальных положений вашего проекта может породить систему с уродливой структурой, которую сложно сопровождать и совершенствовать. Поэтому вы должны думать о производительности уже в процессе разработки приложения.

Старайтесь избегать конструкторских решений, ограничивающих производи­тельность. Труднее всего менять те компоненты, которые определяют взаимодействие модулей с окружающим миром. Главными среди таких компонентов являются АРI, протоколы физического уровня и форматы записываемых данных. Мало того, что эти компоненты впоследствии сложно или невозможно менять, любой из них способен существенно ограничить производительность, которую можно получить от системы.

Изучите влияние на производительность тех проектных решений, которые заложены в ваш API. Создание изменяемого открытого типа может потребовать создания множества ненужных резервных копий (СТАТЬ$/ 24). Точно так же использо­вание наследования в открытом классе, для которого уместнее была бы композиция, навсегда привязывает класс к его суперклассу, а это может искусственно ограничивать производительность данного подкласса (статья: 14) и последний пример: указав в АРI не тип интерфейса, а тип реализующего его класса, так оказываетесь привязаны к определенной реализации этого интерфейса, даже несмотря на то, ЧТО в буду­щем, возможно, будут написаны еще более быстрые его реализации (статья 34).

Влияние архитектуры АРI на производительность велико. Рассмотрим метод getSize из класса jаvа.wt,Component. То, что этот критичный для производительности метод возвращает экземпляр Dimension, а также то, что экземпляры D1mension являются изменяемыми, приходит к тому, что любая реализация этого метода при каждом вызове создает новый экземпляр D1mension. И ХОТ$/, начиная с версии 1.3, создание небольших объектов обходится относительно дешево, бесполезное создание миллионов объектов может нанести производительности приложения реальный ущерб.

В данном случае имеется несколько альтернатив. В идеале класс Dimension должен стать неизменяемым (статья 13). Либо метод getSize можно заменить двумя методами, возвращающими отдельные простые компоненты объекта Dimension.

Идействительно, с целью повышения производительности в версии 1.2 два таких метода были добавлены в интерфейс класса Component. Однако уже существовавший к тому времени клиентский код продолжает пользоваться методом getSize, и его производительность по-прежнему страдает от первоначально принятых проектных решений для API.

 

 

 

Хорошая схема API, как правило, сочетается с хорошей производительностью.

Не стоит искажать API ради улучшения производительности. Проблемы с производительностью, которые заставили вас переделать API, могут исчезнуть с появлением новой платформы или других базовых программ, а вот искаженный АРI и связанная с ним головная боль останутся с вами навсегда.

После того как вы тщательно спроектировали программу и выстроили четкую, краткую и хорошо структурированную ее реализацию, можно подумать об оптими­зации, если, конечно, вы еще не удовлетворены производительность программы. Напомним два правила Джексона: "не делайте этого" и "не делайте этого пока (для экспертов)". Он мог бы добавить еще одно: измеряйте производительность до и после попытки ее оптимизации.

Возможно, вы будете удивлены, но нередко попытки оптимизации не оказывают поддающегося измерению влияния на производительность, иногда они даже ухудшают ее. Основная причина заключается в том, что сложно догадаться, где программа теряет время. Та часть программы, которую вы считаете медленной, может оказаться ни при чем, и вы зря потратите время, пытаясь ее оптимизировать. Общее правило гласит, что 80% времени программы теряют на 20% своего кода.

Средства профилирования помогут вам определить, где именно следует сосредо­точить усилия по оптимизации. Подобные инструменты предоставляют вам информа­цию о ходе выполнения программы, например: сколько примерно времени требуется каждому методу, сколько раз он был вызван. Это укажет вам объект для настройки, а также может предупредить вас о необходимости замены самого алгоритма. Если в вашей программе скрыт алгоритм с квадратичной (или еще худшей) зависимостью, никакие настройки эту проблему не решат. Следовательно, вам придется заменить алгоритм более эффективным. Чем больше в системе программного кода, тем большее значение имеет работа с профилировщиком. Это все равно, что искать иголку в стоге сена: чем больше стог, тем больше пользы от металлоискателя. Java 2 SDK поставля­ется с простым профилировщиком, несколько инструментов посложнее можно купить отдельно.

Задача определения эффекта оптимизации для платформы Java стоит острее, чем для традиционных платформ, по той причине, что язык программирования Java не име­ет четкой модели производительности (performance model). Нет четкого определения относительной стоимости различных базовых операций. "Семантический разрыв" между тем, что пишет программист, и тем, что выполняется центральным процессо­ром, здесь гораздо значительнее, чем у традиционных компилируемых языков, и это сильно усложняет надежное предсказание того, как будет влиять на производитель­ность какая-либо оптимизация. Существует множество мифов о производительности, которые на поверку оказываются полуправдой, а то и совершенной ложью.

Помимо того, что модель производительности плохо определена, она меняется от одной реализации JVM к другой и даже от версии к версии. Если вы будете запускать свою программу в разных реализациях JVM, про следите эффект от вашей оптимиза­ции для каждой из этих реализаций. Иногда в отношении производительности вам придется идти на компромисс между различными реализациями JVM.

 

 

 

 

Подведем итоги. Не старайтесь писать быстрые программы - лучше пишите хорошие, тогда у вас появится и скорость. Проектируя системы, обязательно думайте о производительности, особенно если вы работаете над API, протоколами нижних уровней и форматами записываемых данных. Закончив построение системы, измерьте ее производительность. Если скорость приемлема, ваша работа завершена. Если нет, локализуйте источник проблем с помощью профилировщика и оптимизируйте соот­ветствующие части системы. Первым шагом должно быть исследование выбранных алгоритмов: никакая низкоуровневая оптимизация не компенсирует плохой выбор алгоритма. При необходимости повторите эту процедуру, измеряя производительность после каждого изменения, пока не будет получен приемлемый результат.