Эффективность и технологичность.

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

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

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

И тем более не следует «платить» за увеличение эффективности сниже­нием технологичности 'разрабатываемого программного обеспечения. Ис­ключения возможны лишь при очень жестких требованиях и наличии соот­ветствующего контроля за качеством.

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

машинно-зависимые, т. е. ориентированные на конкретный машинный
язык, выполняют оптимизацию кодов на уровнемашинных команд, напри­
мер, исключение лишних пересылок, использование более эффективных ко­
манд и т. п.;

машинно-независимые выполняют оптимизацию на уровне входного
языка, например, вынесение вычислений константных (независящих от ин­
декса цикла) выражений из циклов и т. п.

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

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

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

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

Также следует помнить, что при передаче структурных данных в под­программу «по значению» копии этих данных размещаются в стеке. Из­бежать копирования иногда удается, если передавать данные «по ссылке», но как неизменяемые (описанные const). В последнем случае в стеке размеща­ется только адрес данных, например:

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

• выносить вычисление константных, т. е. не зависящих от параметров
цикла, выражений из циклов;

• избегать «длинных» операций умножения и деления, заменяя их сло­
жением, вычитанием и сдвигами;

• минимизировать преобразования типов в выражениях;

• оптимизировать запись условных выражений - исключать лишние
проверки;

• исключать многократные обращения к элементам массивов по индек­
сам (особенно многомерных, так как при вычислении адреса элемента ис­
пользуются операции умножения на значение индексов) - первый раз прочи­
тав из памяти элемент массива, следует запомнить его в скалярной перемен­
ной и использовать в нужных местах;


о<) В этом цикле операции умножения и обращения к элементу S[k] выпол­няются 10000 раз. Оптимизируем цикл, исподьзуя, что 320 = 2^ + 26:

^ «избегать использования различных типов в выражении и т. п. Рассмотрим следующие примеры. Пример2.2. Пусть имеется цикл следующей структуры (Pascal):

В результате вместо 10000 операций умножения будут выполняться 200 операций сдвига, а их время приблизительно сравнимо со временем выпол­нения операции сложения. Обращение к элементу массива S[k] будет выпол­нено один раз.

Пример 2.3.Пусть имеется цикл, в теле которого реализовано сложное условие:

Обратите внимание на то, что в примере 2.2 понять, что делает програм­ма, стало сложнее, а в примере 2.3 - практически нет. Следовательно, опти­мизация, выполненная в первом случае, может ухудшить технологичность программы, а потому не очень желательна.