Некоторые задачи часто могут быть выполнены более эффективно. Например, рассмотрим следующую программу на языке Си, которая суммирует все целые числа от 1 до N:
int i, sum = 0; for (i = 1; i N; i++) sum += i; printf («sum: %dn», sum);
Этот код может быть (подразумевая, что нет переполнения) переписан, используя математическую формулу, в следующем виде:
int sum = (N * (N+1)) / 2; printf («sum: %dn», sum);
Термин «оптимизация» обычно подразумевает, что система сохраняет ту же самую функциональность. Однако, значительное улучшение производительности часто может быть достигнуто с помощью решения насущной проблемы и удаления избыточной функциональности. Например, если обоснованно допустить, что программе не требуется поддерживать более, чем (скажем) 100 элементов при вводе, то возможно использовать статическое выделение памяти вместо медленного динамического.
Уступки (tradeoffs) [ ]
Оптимизация в основном фокусируется на одиночном или повторном времени выполнения, использовании памяти, дискового пространства, пропускной способности или некотором другом ресурсе. Это обычно требует Различные области [ ]
Как ускорить After Effects. 9 Советов по оптимизации программы
В исследовании операций, оптимизация — это проблема определения входных значений функции, при которых она имеет максимальное или минимальное значение. Иногда на эти значения накладываются ограничения, такая задача известна как ограниченная оптимизация.
В программировании, оптимизация обычно обозначает модификацию кода и его Узкие места [ ]
Для оптимизации требуется найти Простейшие приёмы оптимизации программ по затратам процессорного времени [ ]
Оптимизация по затратам процессорного времени особенно важна для расчетных программ, в которых большой удельный вес имеют математические вычисления. Здесь перечислены некоторые приемы оптимизации, которые может использовать программист при написании исходного текста программы.
Инициализация объектов данных [ ]
Во многих программах какую-то часть объектов данных необходимо инициализировать, то есть присвоить им начальные значения. Такое присваивание выполняется либо в самом начале программы, либо, например, в конце цикла. Правильная инициализация объектов позволяет сэкономить драгоценное процессорное время. Так, например, если речь идет об инициализации массивов, использование цикла, скорее всего, будет менее эффективным, чем объявление этого массива прямым присвоением.
Программирование арифметических операций [ ]
В том случае когда значительная часть времени работы программы отводится арифметическим вычислениям, немалые резервы повышения скорости работы программы таятся в правильном программировании арифметических (и логических) выражений. Важно понимать, что различные арифметические операции значительно различаются по быстродействию.
Самыми быстрыми являются операции сложения и вычитания. Более медленным является умножение, затем идет деление. Относительно много времени тратится на обращение к подпрограммам. Быстродействие также зависит и от типа операндов. Например, в языке a x 4 + b x 3 + c x 2 + d x + e +bx^+cx^+dx+e>
Оптимизации раскроя программа №1
При условии, что вычисление степени производится перемножением основания определенное число раз, нетрудно найти, что в этом выражении содержится 10 умножений («медленных» операций) и 4 сложения («быстрых» операций). Это же самое выражение можно записать в виде:
( ( ( a x + b ) x + c ) x + d ) x + e
Такая форма записи называется схемой Горнера. В этом выражении 4 умножения и 4 сложения. Общее количество операций сократилось почти в два раза, соответственно уменьшится и время вычисления многочлена.
Циклы [ ]
Различается и время выполнения циклов разного типа. Время выполнения цикла со счетчиком и цикла с постусловием при всех прочих равных условиях совпадает, цикл с предусловием выполняется несколько дольше (примерно на 20-30 %).
При использовании вложенных циклов следует иметь в виду, что затраты процессорного времени на обработку такой конструкции могут зависеть от порядка следования вложенных циклов. Рассмотрим, например, такой вложенный цикл со счетчиком на языке
for j := 1 to 100000 do for k := 1 to 1000 do a := 1;
Этот цикл выполняется примерно на 10 % дольше, чем следующий:
for j := 1 to 1000 do for k := 1 to 100000 do a := 1;
На первый взгляд, и в первом и во втором случае 10 000 000 раз выполняется оператор присваивания, и затраты времени на это должны быть одинаковы в обеих случаях. Но это не так.
Объясняется наше наблюдение тем, что инициализации цикла, то есть обработка процессором его заголовка с целью определения начального и конечного значений счетчика, а также шага приращения счетчика требует времени. В первом случае 1 раз инициализируется внешний цикл и 100 000 раз — внутренний, то есть всего выполняется 100 001 инициализация. Во втором случае, как нетрудно подсчитать, таких инициализаций оказывается всего лишь 1001.
Аналогично ведут себя вложенные циклы с предусловием и с постусловием. Можно сделать вывод, что при программировании вложенных циклов по возможности следует делать цикл с наибольшим числом повторений самым внутренним, а цикл с наименьшим числом повторений — самым внешним.
При вычислении сумм часто используются циклы, содержащие одинаковые операции, относящиеся к каждому слагаемому. Это может быть, например, общий множитель (язык
summa := 0; for i := 1 to 1000 do summa := summa + a * x[i];
Очевидно, что другая форма записи этого цикла оказывается более экономной:
summa := 0; for i := 1 to 1000 do summa := summa + x[i]; summa := a * summa;
Инвариантные фрагменты кода [ ]
Тема оптимизации инвариантных фрагментов кода тесно связана с проблемой оптимального программирования циклов. Внутри цикла могут встречаться выражения, фрагменты которых никак не зависят от управляющей переменной цикла. Их называют инвариантными фрагментами кода. Современные компиляторы часто определяют наличие таких фрагментов и выполняют их автоматическую оптимизацию. Такое возможно не всегда, и иногда производительность программы зависит целиком от того, как запрограммирован цикл. В качестве примера рассмотрим следующий фрагмент программы (язык
for i := 1 to n do begin . for k := 1 to p do for m := 1 to q do begin a[k, m] := Sqrt(x * k * m — i) + Abs(u * i — x * m + k); b[k, m] := Sin(x * k * i) + Abs(u * i * m + k); end; . am := 0; bm := 0; for k := 1 to p do for m := 1 to q do begin am := am + a[k, m] / c[k]; bm := bm + b[k, m] / c[k]; end; end;
Здесь инвариантными фрагментами кода являются слагаемое Sin(x * k * i) в первом цикле по переменной m и операция деления на элемент массива c[k] во втором цикле по m. Значения синуса и элемента массива не изменяются в цикле по переменной m, следовательно, в первом случае можно вычислить значение синуса и присвоить его вспомогательной переменной, которая будет использоваться в выражении, находящемся внутри цикла. Во втором случае можно выполнить деление после завершения цикла по m. Таким образом можно существенно сократить количество трудоемких арифметических операций.
См. также [ ]
- Принцип KISS
- Граф передачи управления
- Низкоуровневая виртуальная машина
- Симулятор
- Ссылки [ ]
- Programming Optimization
- C, C++ optimization
- C optimization tutorial
- Software Optimization at Link-time And Run-time
- Profiling and optimizing Ruby code
- Никлаус Вирт . A Plea for Lean Software
- Description from the Portland Pattern Repository
- Улучшаем производительность приложения путём перераспределения кода
- ПОИСК ГЛОБАЛЬНОГО ОПТИМУМА
- Оптимизация 64-битных программ
Литература [ ]
- Jon Bentley . Writing Efficient Programs. ISBN 0-13-970251-2
- ISBN 0-201-89683-4
- ISBN 0-201-89684-2
- ISBN 0-201-89685-0
- ISBN 5-94723-702-4
Эта страница использует содержимое раздела Википедии на русском языке. Оригинальная статья находится по адресу: Оптимизация (информатика). Список первоначальных авторов статьи можно посмотреть в истории правок. Эта статья так же, как и статья, размещённая в Википедии, доступна на условиях CC-BY-SA .
Источник: vlab.fandom.com
Оптимизация
Аннотация: Задачи оптимизации. Виды оптимизирующих преобразований. Представления программы, используемые в оптимизирующих преобразованиях. Примеры оптимизирующих преобразований.
Оптимизация
Под оптимизацией понимают последовательность эквивалентных преобразований исходной программы, уменьшающих ее стоимость . Как набор, так и порядок выполнения этих преобразований зависят от того, что считается стоимостью программы. В качестве такой стоимости могут выступать, например, среднее время работы, объем кода и т.д. Эффективность оптимизации также зависит от отношения эквивалентности и от размера участка экономии, на котором эта оптимизация проводится (обычно оптимизированной программе разрешается иметь большую область определения , чем исходной). За счет оптимизации невозможно добиться существенного улучшения алгоритма программы, можно только говорить об улучшении реализации этого алгоритма. В удачных случаях оптимизация может ускорить программу в несколько раз. Полезность применения оптимизации обусловлена следующими причинами:
- Распространением языков сверхвысокого уровня, языков спецификации и систем проектирования. Как правило, подобные системы порождают программы на некотором языке высокого уровня. В этом случае оптимизация может нейтрализовать избыточность такого порождения, приблизив качество сгенерированной программы к ручному программированию.
- Необходимостью поддержки и сопровождения готовой программы. Такие требования зачастую приводят к тому, что программисты используют не самые эффективные решения в целях улучшения наглядности или легкости сопровождения программ (очень часто соображения эффективности и сопровождаемости противоречат друг другу). В то же время недостатки эффективности могут быть исправлены оптимизатором при генерации окончательной программы.
- Усилением контроля семантических ошибок. Такие ошибки требуют специального анализа исходной программы, который может являться побочным результатом действий оптимизатора.
Виды оптимизации
Существует много различных классификаций оптимизирующих преобразований. Здесь мы рассмотрим классификации по уровню представления программы и по размеру участка экономии.
В зависимости от уровня представления программы различают следующие виды оптимизации:
- Оптимизацию на уровне исходного языка. При этом в результате трансформации получается программа, записанная в том же самом языке.
- Машинно-независимую оптимизацию. В этом случае преобразованию подвергается программа на уровне машинно-независимого промежуточного представления, общего для группы входных или машинных языков.
- Машинно-зависимую оптимизацию, то есть оптимизацию на уровне машинного языка.
С точки зрения эффективности наиболее предпочтительной является машинно-зависимая оптимизация , поскольку именно с ее помощью можно учесть особенности конкретной вычислительной среды, однако машинно-зависимый оптимизатор непереносим. С другой стороны, преобразование программы на уровне исходного языка позволяет получить более эффективную программу, допускающую дальнейшее развитие и сопровождение. Наконец, машинно-независимая оптимизация на уровне промежуточного представления является компромиссом между этими двумя крайними случаями.
Другим важным для качества оптимизации соображением является также размер участка экономии, то есть того фрагмента программы, в рамках которого производится оптимизирующее преобразование. Чем больше участок экономии, тем больше информации о свойствах программы доступно оптимизатору. Классификация оптимизации относительно участка экономии приведена на слайде.
Источник: intuit.ru
Техника оптимизации программного кода
Раньше, когда оперативная память компьютера исчислялась килобайтами, дисковое пространство десятками мегабайт, а частота процессора мегагерцами, проблемами оптимизации занимались все. Было достаточно сложно написать хорошую программу, которая бы быстро выполнялась на таких скромных системах. Программисты «вылизывали» каждую строчку, добиваясь максимальной эффективности.
А что сейчас? Вычислительные мощности современных компьютеров достигли фантастических (если сравнивать с тем, что было) значений, и даже такие «монстры», как Windows 7 не в силах их затормозить. И зачем нам оптимизировать, если так все нормально работает? Многие так и считают.
Сейчас программирование дошло до такой стадии, что важнее стала скорость написания программ, чем скорость их работы. Потому что скорость их работы будет заведомо высокой. Но это относится лишь к обычным прикладным программам.
Совсем другое дело драйвера (которые мало изменились еще со времен DOS), программы обработки аудио, видео, и графики, генерации паролей… В них про оптимизацию забывать ни в коем случае нельзя. Да и в обычных программах она никогда не бывает лишней. Куда приятней использовать более эффективную программу, чем идти в магазин за новым процессором. Или ждать, пока она соизволит загрузится, кому как лучше. Большинство пользователей выбирает первый вариант.
Оптимизация
В оптимизации есть несколько важных моментов:
Оптимизация должна быть естественной. Оптимизированный фрагмент кода должен легко вливаться в программу, не нарушая логики ее работы. Он должен легко вводится в программу, изменятся или удаляться из нее.
Оптимизация должна приносить существенный прирост производительности. Оптимизированная программа должна работать минимум на 20%-30% эффективней, чем ее неоптимизированный аналог, иначе она теряет смысл. Зачем мучится и вносить изменения в уже готовый код, если результата это практически не даст?
Разработка (и отладка) критических областей не должна увеличивать время разработки программы более чем на 10%-15%.
Как уже писалось ранее, сейчас на первый план выходит скорость разработки программ, так что все же не нужно отставать от остальной массы программистов. Себе же хуже.
Так же, перед тем как писать оптимизированный вариант, полезно иметь его неоптимизированный аналог. Обычно, оптимизированный код очень тяжел для восприятия, и если после его внедрения в программе появятся ошибки, то, подставив вместо него его менее эффективного собрата, мы можем определить, кто виноват в ошибках.
Логика оптимизации программного кода
Теперь перейдем к самой философии оптимизации. Считается, что критические области следует писать на ассемблере, поскольку он дает наивысшую скорость работы. Но зачастую результат работы оптимизирующего компилятора работает медленней своего ассемблерного аналога на 2%-7% (не более 20%). И стоило ли из-за такого мизерного прироста тратить время на разработку ассемблерной реализации? Из этого следует, что намного лучше оптимизировать код, написанный на языке высокого уровня, оптимизировать саму логику работы программы.
Основные постулаты оптимизации:
- Начинать оптимизацию нужно с самых «узких» мест программы. Если мы будем оптимизировать те места, где и без нашего вмешательства все быстро работает, то прирост производительности будет минимален. Это основной закон оптимизации, от него мы и будем отталкиваться, разбирая остальные.
- Оптимизировать лучше те места, которые регулярно повторяются в ходе работы. Этот закон относится к циклам и подпрограммам. Если можно хотя бы немного оптимизировать цикл, то делайте это не задумываясь. Если в одной итерации мы добьемся прироста в 2%, то после 1000 повторений это уже будет достаточно большое значение.
- Старайтесь не слишком злоупотреблять оптимизацией единичных операций. Этот закон – своеобразное продолжение предыдущего. Оптимизируя фрагмент, который будет вызван лишь один раз, мы вряд ли добьемся ощутимого прироста (но если эффект будет ощутим (>10%, что бывает крайне редко), то оптимизация лишней не будет).
- Используйте ассемблер только там, где скорость работы очень важна. Как уже писалось ранее, сейчас ассемблер не дает огромного прироста скорости. Поэтому использовать его стоит лишь в самых «узких» местах программы.
- Задумывайтесь над оптимизацией. Неправильная оптимизация может даже навредить программе, увеличить время ее разработки, практически не уменьшив скорость ее работы.
Конечно, в современном мире сверхбыстрых вычислений на первый план выходит скорость разработки программ. Но все же, не стоит забывать об оптимизации, которая, несмотря на общепринятое мнение, никогда не уходила на второй.
Источник: cppstudio.com