Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
информатика распечатать.doc
Скачиваний:
4
Добавлен:
10.12.2018
Размер:
164.86 Кб
Скачать

2. Некоторые приемы оптимизации программ

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

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

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

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

  1. Оптимизация, не зависящая от компилятора.

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

  1. Инициализация объектов данных.

Во многих программах какую-то часть объектов данных необходимо инициализировать, то есть присваивать им начальные значения. Такое присваивание выполняется либо в самом начале программы, либо, например, в начале цикла. Правильная инициализация объектов данных позволяет сэкономить драгоценное процессорное время. Так, например, если речь идет об инициализации массивов, использование цикла, скорее всего, будет менее эффективным, чем объявление этого массива типизированной константой.

  1. Программирование арифметических операций.

В том случае, когда значительная часть времени работы программы отводится арифметическим вычислениям, немалые резервы повышения скорости работы программы тратятся в правильном программировании арифметических (и логических) выражений. Важно понимать, что различные арифметические операции значительно различаются по быстродействию. Самыми быстрыми являются операции сложения и вычитания. Более медленным является умножение, затем идет деление. Относительно много времени тратится на обращение к подпрограммам. Быстродействие также зависит от типа операндов.

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

Рассмотрим пример. Пусть необходимо вычислить многочлен 4-й степени:

ax4 + bx3 + cx2 + cx + e

При условии, что вычисление степени производится перемножением основания определенное число раз, нетрудно найти, что в этом выражении содержится 10 умножений (“медленных” операций). Однако это же выражение можно записать в следующем виде:

(((ax + b) * x + c) * x + d) * x +e

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

  1. Циклы

Различается и время выполнения циклов. Время выполнения цикла со счетчиком и цикла постусловием при всех прочих равных условиях совпадает, цикл с предусловием выполняется несколько дольше (примерно на 20-30%).

При использовании вложенных циклов следует иметь в виду, что затраты процессорного времени на выполнение такой конструкции могут зависеть от порядка следования вложенных циклов. Так, например, вложенный цикл со счетчиком

for j := 1 to 100000 for k := 1 to 1000

a := 1 next k next j

выполняется примерно на 10 % дольше, чем цикл

for j := i to 1000 for k := 1 to 100000

a := 1 next k next j

На первый взгляд, и в первом и во втором случае 10 000 000 раз выполняется оператор присваивания, и затраты времени на это должны быть одинаковы в обоих случаях. Но это не так. Объясняется это тем, что инициализация цикла, то есть обработка процессором его заголовка с целью определения начального и конечного значения счетчика, а также шага приращения счетчика требует времени. В первом случае 1 раз инициализируется внешний цикл и 100 000 раз — внутренний, то есть всего выполняется 100 001 инициализаций. Вo втором случае, как нетрудно подсчитать, таких инициализаций оказывается тишь 1001.

Аналогично ведут себя вложенные циклы с предусловием и с постусловием. Можно сделать вывод, что при программировании вложенных циклов по возможности следует делать цикл с наибольшим числом повторений самым внутренним, а цикл с наименьшим числом повторений — самым внешним.

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

Summ = 0 For i=1 to 1000 Summ = Summ + a * x[i] Next i

Очевидно, что другая форма записи этого цикла оказывается более экономной:

Summ = 0 For i=1 to 1000 Summ = Summ +* x[i]

Next I Summ = a * Summ

так как она содержит всего одно умножение при том же числе сложений, против 1000 умножений в первом случае.

  1. Инвариантные фрагменты кода

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

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

1