Оптимизация программы
В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
В информатике , программ оптимизация оптимизация кода или оптимизация программного обеспечения — это процесс модификации программной системы, чтобы некоторые ее аспекты работали более эффективно или использовали меньше ресурсов. [1] В общем, компьютерную программу можно оптимизировать так, чтобы она выполнялась быстрее, или чтобы она могла работать с меньшим объемом памяти или других ресурсов, или потреблять меньше энергии.
Общий
[ редактировать ]Хотя слово «оптимизация» имеет тот же корень, что и слово «оптимальный», процесс оптимизации редко приводит к созданию действительно оптимальной системы. Как правило, систему можно сделать оптимальной не в абсолютном выражении, а только по отношению к заданной метрике качества, которая может контрастировать с другими возможными метриками. В результате оптимизированная система обычно будет оптимальной только для одного приложения или для одной аудитории. Можно сократить время, необходимое программе для выполнения некоторой задачи, за счет увеличения потребления памяти. В приложении, где объем памяти ограничен, можно намеренно выбрать более медленный алгоритм , чтобы использовать меньше памяти. Часто не существует универсальной конструкции, которая бы хорошо работала во всех случаях, поэтому инженеры идут на компромиссы, чтобы оптимизировать характеристики, представляющие наибольший интерес. Кроме того, усилия, необходимые для того, чтобы сделать часть программного обеспечения полностью оптимальной – не поддающейся дальнейшему улучшению – почти всегда превышают разумные преимущества, которые могут быть получены; поэтому процесс оптимизации может быть остановлен до того, как будет достигнуто полностью оптимальное решение. К счастью, зачастую самые большие улучшения происходят на ранних этапах процесса.
Даже для заданного показателя качества (например, скорости выполнения) большинство методов оптимизации только улучшают результат; они не претендуют на достижение оптимального результата. Супероптимизация — это процесс поиска действительно оптимального результата.
Уровни оптимизации
[ редактировать ]Оптимизация может происходить на нескольких уровнях. Обычно более высокие уровни оказывают большее влияние, и их труднее изменить на более позднем этапе проекта, что требует значительных изменений или полного переписывания, если их необходимо изменить. Таким образом, оптимизация обычно может осуществляться путем уточнения от большего к меньшему, при этом первоначальный выигрыш будет больше и достигается с меньшими затратами труда, а более поздний выигрыш будет меньше и потребует больше работы. Однако в некоторых случаях общая производительность зависит от производительности очень низкоуровневых частей программы, и небольшие изменения на позднем этапе или на раннем этапе рассмотрения низкоуровневых деталей могут иметь огромное влияние. Обычно некоторое внимание уделяется эффективности на протяжении всего проекта (хотя она значительно варьируется), но основная оптимизация часто считается доработкой, которую следует проводить поздно, если вообще когда-либо. В долгосрочных проектах обычно существуют циклы оптимизации, когда улучшение одной области выявляет ограничения в другой, и они обычно сокращаются, когда производительность становится приемлемой или выгоды становятся слишком маленькими или дорогостоящими.
Поскольку производительность является частью спецификации программы, программа, которая неприемлемо медленна, не подходит для этой цели: видеоигра с частотой 60 Гц (кадров в секунду) приемлема, но 6 кадров в секунду неприемлемо прерывисты. Производительность учитывается с самого начала, чтобы гарантировать, что система способна обеспечить достаточную производительность, а ранние прототипы должны иметь примерно приемлемую производительность, чтобы была уверенность в том, что окончательная система (с оптимизацией) достигнет приемлемой производительности. Иногда об этом умалчивают, полагая, что оптимизацию всегда можно провести позже, в результате чего прототипы систем работают слишком медленно (часто на порядок и более) и системы, которые в конечном итоге терпят неудачу, потому что они архитектурно не могут достичь своих целей по производительности, например как Intel 432 (1981 г.); или те, которые требуют многих лет работы для достижения приемлемой производительности, например Java (1995), которая достигла приемлемой производительности только с помощью HotSpot (1999). Степень изменения производительности между прототипом и производственной системой и то, насколько она поддается оптимизации, может быть существенным источником неопределенности и риска.
Уровень дизайна
[ редактировать ]На самом высоком уровне проект может быть оптимизирован для наилучшего использования доступных ресурсов с учетом целей, ограничений и ожидаемого использования/нагрузки. Архитектурный дизайн системы в огромной степени влияет на ее производительность. Например, система, которая привязана к задержке сети (где задержка сети является основным ограничением общей производительности), будет оптимизирована для минимизации сетевых отключений, в идеале делая один запрос (или не делая запросов, как в протоколе push ), а не несколько поездки туда и обратно. Выбор конструкции зависит от целей: при разработке компилятора , если быстрая компиляция является ключевым приоритетом, однопроходный компилятор работает быстрее, чем многопроходный компилятор (при условии одинаковой работы), но если целью является скорость вывода кода, более медленный многопроходный компилятор лучше достигает цели, хотя сам по себе он занимает больше времени. Выбор платформы и языка программирования происходит на этом уровне, и их изменение часто требует полной переписывания, хотя модульная система может позволить переписать только некоторые компоненты — например, программа Python может переписать критически важные для производительности разделы на C. В распределенном система, выбор архитектуры( клиент-сервер , одноранговая сеть и т. д.) возникает на уровне проекта, и его может быть сложно изменить, особенно если все компоненты не могут быть заменены синхронно (например, старые клиенты).
Алгоритмы и структуры данных
[ редактировать ]Учитывая общий дизайн, на следующем этапе следует хороший выбор эффективных алгоритмов и структур данных , а также эффективная реализация этих алгоритмов и структур данных. После проектирования выбор алгоритмов и структур данных влияет на эффективность больше, чем любой другой аспект программы. Как правило, структуры данных сложнее изменить, чем алгоритмы, поскольку предположения о структуре данных и предположения о ее производительности используются во всей программе, хотя это можно свести к минимуму за счет использования абстрактных типов данных в определениях функций и сохранения ограничений на определения конкретных структур данных. в несколько мест.
Для алгоритмов это в первую очередь состоит в обеспечении того, чтобы алгоритмы были постоянными O(1), логарифмическими O(log n ), линейными O( n ) или, в некоторых случаях, лог-линейными O( n log n ) на входе (оба в пространстве и время). Алгоритмы квадратичной сложности O( n 2 ) не масштабируются, и даже линейные алгоритмы вызывают проблемы при неоднократном вызове и обычно заменяются константными или логарифмическими, если это возможно.
Помимо асимптотического порядка роста, имеют значение постоянные факторы: асимптотически более медленный алгоритм может быть быстрее или меньше (поскольку проще), чем асимптотически более быстрый алгоритм, когда они оба сталкиваются с небольшими входными данными, что может иметь место в действительности. Часто гибридный алгоритм обеспечивает наилучшую производительность, поскольку этот компромисс меняется с размером.
Общий метод повышения производительности — избегать работы. Хорошим примером является использование быстрого пути для распространенных случаев, позволяющего повысить производительность за счет исключения ненужной работы. Например, используя простой алгоритм раскладки текста для латинского текста, переходя на сложный алгоритм раскладки только для сложных скриптов, таких как деванагари . Еще одним важным методом является кэширование, в частности мемоизация , позволяющая избежать избыточных вычислений. Из-за важности кэширования в системе часто имеется много уровней кэширования, что может вызвать проблемы из-за использования памяти и проблемы корректности из-за устаревших кэшей.
Уровень исходного кода
[ редактировать ]Помимо общих алгоритмов и их реализации на абстрактной машине, выбор конкретного уровня исходного кода может иметь существенное значение. Например, в ранних компиляторах C while(1)
был медленнее, чем for(;;)
для безусловного цикла, потому что while(1)
вычислил 1, а затем выполнил условный переход, который проверял, истинно ли это, в то время как for (;;)
был безусловный прыжок. Некоторые оптимизации (например, эта) в настоящее время могут быть выполнены путем оптимизации компиляторов . Это зависит от исходного языка, целевого машинного языка и компилятора и может быть трудным для понимания или прогнозирования и может меняться со временем; это ключевой момент, где понимание компиляторов и машинного кода может повысить производительность. Инвариантное к циклу движение кода и оптимизация возвращаемого значения являются примерами оптимизаций, которые уменьшают потребность во вспомогательных переменных и могут даже привести к повышению производительности за счет исключения обходных оптимизаций.
Уровень сборки
[ редактировать ]Между уровнем исходного кода и уровнем компиляции директивы и флаги сборки могут использоваться для настройки параметров производительности в исходном коде и компиляторе соответственно, например, использование определений препроцессора для отключения ненужных функций программного обеспечения, оптимизации для конкретных моделей процессоров или возможностей оборудования или прогнозирования ветвления . например. такие как BSD Portes Системы распространения программного обеспечения на основе исходного кода , и Gentoo Portage , могут воспользоваться преимуществами этой формы оптимизации.
Уровень компиляции
[ редактировать ]Использование оптимизирующего компилятора обеспечивает оптимизацию исполняемой программы по крайней мере настолько, насколько может предсказать компилятор.
Уровень сборки
[ редактировать ]На самом низком уровне написание кода с использованием языка ассемблера , разработанного для конкретной аппаратной платформы, может дать наиболее эффективный и компактный код, если программист воспользуется преимуществами полного репертуара машинных инструкций . По этой причине многие операционные системы , используемые во встроенных системах, традиционно писались на ассемблере. Программы (кроме очень маленьких программ) редко пишутся от начала до конца на ассемблере из-за затрат времени и средств. Большинство из них скомпилированы с языка высокого уровня на ассемблер и оптимизированы вручную. Когда эффективность и размер менее важны, большие части могут быть написаны на языке высокого уровня.
С появлением более современных оптимизирующих компиляторов и большей сложностью современных процессоров труднее написать более эффективный код, чем тот, который генерирует компилятор, и лишь немногие проекты нуждаются в этом «окончательном» этапе оптимизации.
Большая часть кода, написанного сегодня, предназначена для запуска на максимально возможном количестве машин. Как следствие, программисты и компиляторы не всегда используют преимущества более эффективных инструкций, предоставляемых новыми процессорами, или особенности старых моделей. Кроме того, ассемблерный код, настроенный для конкретного процессора без использования таких инструкций, все равно может быть неоптимальным на другом процессоре, ожидая другой настройки кода.
Обычно сегодня вместо того, чтобы писать на языке ассемблера, программисты используют дизассемблер для анализа выходных данных компилятора и изменения исходного кода высокого уровня, чтобы его можно было скомпилировать более эффективно, или понять, почему он неэффективен.
Время выполнения
[ редактировать ]Компиляторы «точно в срок» могут создавать индивидуальный машинный код на основе данных времени выполнения за счет накладных расходов на компиляцию. Этот метод восходит к самым ранним механизмам регулярных выражений и получил широкое распространение благодаря Java HotSpot и V8 для JavaScript. В некоторых случаях адаптивная оптимизация может обеспечить оптимизацию времени выполнения , превосходящую возможности статических компиляторов, путем динамической настройки параметров в соответствии с фактическими входными данными или другими факторами.
Оптимизация на основе профиля — это метод оптимизации компиляции с опережением времени (AOT), основанный на профилях времени выполнения и похожий на статический аналог динамического метода адаптивной оптимизации в «среднем случае».
Самомодифицирующийся код может изменяться в ответ на условия времени выполнения с целью оптимизации кода; это было более распространено в программах на языке ассемблера.
Некоторые конструкции ЦП могут выполнять некоторую оптимизацию во время выполнения. Некоторые примеры включают выполнение вне очереди , спекулятивное выполнение , конвейеры команд и предсказатели ветвления . Компиляторы могут помочь программе использовать преимущества этих функций ЦП, например, посредством планирования инструкций .
Платформозависимая и независимая оптимизация
[ редактировать ]Оптимизацию кода также можно разделить на платформо -зависимые и платформо-независимые методы. Хотя последние эффективны на большинстве или на всех платформах, платформо-зависимые методы используют определенные свойства одной платформы или полагаются на параметры, зависящие от одной платформы или даже от одного процессора. Поэтому может потребоваться написание или создание разных версий одного и того же кода для разных процессоров. Например, в случае оптимизации на уровне компиляции независимые от платформы методы представляют собой общие методы (такие как развертывание цикла , сокращение вызовов функций, эффективные процедуры использования памяти, сокращение условий и т. д.), которые одинаково влияют на большинство архитектур ЦП. способ. Отличный пример независимой от платформы оптимизации был показан с помощью внутреннего цикла for: было замечено, что цикл с внутренним циклом for выполняет больше вычислений в единицу времени, чем цикл без него или цикл с внутренним циклом while. [2] Как правило, они служат для уменьшения общей длины пути инструкций, необходимой для завершения программы, и/или уменьшения общего использования памяти во время процесса. С другой стороны, платформо-зависимые методы включают планирование инструкций, параллелизм на уровне инструкций , параллелизм на уровне данных, методы оптимизации кэша (т. е. параметры, которые различаются на разных платформах), а оптимальное планирование инструкций может быть разным даже на разных процессорах. та же архитектура.
Снижение прочности
[ редактировать ]Вычислительные задачи могут выполняться несколькими различными способами с разной эффективностью. Более эффективная версия с эквивалентной функциональностью известна как снижение прочности . Например, рассмотрим следующий фрагмент кода C , целью которого является получение суммы всех целых чисел от 1 до N :
int i, sum = 0;
for (i = 1; i <= N; ++i) {
sum += i;
}
printf("sum: %d\n", sum);
Этот код можно (при условии отсутствия арифметического переполнения ) переписать с использованием математической формулы, например:
int sum = N * (1 + N) / 2;
printf("sum: %d\n", sum);
Оптимизация, иногда выполняемая автоматически оптимизирующим компилятором, заключается в выборе метода ( алгоритма ), который более эффективен в вычислительном отношении, сохраняя при этом ту же функциональность. См. «Алгоритмическая эффективность» для обсуждения некоторых из этих методов. Однако значительного улучшения производительности часто можно добиться, удалив посторонние функции.
Оптимизация не всегда является очевидным или интуитивно понятным процессом. В приведенном выше примере «оптимизированная» версия на самом деле могла бы быть медленнее, чем исходная версия, если бы N было достаточно маленьким, а конкретное оборудование выполняло операции сложения и цикла намного быстрее, чем умножение и деление.
Компромиссы
[ редактировать ]Однако в некоторых случаях оптимизация основана на использовании более сложных алгоритмов, использовании «особых случаев» и особых «хитростей» и выполнении сложных компромиссов. «Полностью оптимизированную» программу может быть труднее понять и, следовательно, она может содержать больше ошибок , чем неоптимизированные версии. Помимо устранения очевидных антипаттернов, некоторые оптимизации на уровне кода снижают удобство сопровождения.
Оптимизация обычно фокусируется на улучшении только одного или двух аспектов производительности: времени выполнения, использования памяти, дискового пространства, пропускной способности, энергопотребления или какого-либо другого ресурса. Обычно это требует компромисса – когда один фактор оптимизируется за счет других. Например, увеличение размера кэша повышает производительность во время выполнения, но также увеличивает потребление памяти. Другие распространенные компромиссы включают ясность и краткость кода.
Бывают случаи, когда программист, выполняющий оптимизацию, должен решить улучшить программное обеспечение для некоторых операций, но за счет снижения эффективности других операций. Эти компромиссы иногда могут носить нетехнический характер – например, когда конкурент опубликовал результаты тестов , которые необходимо превзойти, чтобы повысить коммерческий успех, но, возможно, это влечет за собой бремя, заключающееся в том, что обычное использование программного обеспечения становится менее эффективным. Такие изменения иногда в шутку называют пессимизациями .
Узкие места
[ редактировать ]Оптимизация может включать поиск узкого места в системе – компонента, который является ограничивающим фактором производительности. С точки зрения кода, это часто будет « горячая точка» — критическая часть кода, которая является основным потребителем необходимого ресурса, — хотя это может быть и другой фактор, например задержка ввода-вывода или пропускная способность сети.
В информатике потребление ресурсов часто подчиняется степенному закону распределения, и принцип Парето можно применить к оптимизации ресурсов, заметив, что 80% ресурсов обычно используются 20% операций. [3] В разработке программного обеспечения часто лучшим приближением является то, что 90% времени выполнения компьютерной программы тратится на выполнение 10% кода (в этом контексте это известно как закон 90/10).
Более сложные алгоритмы и структуры данных хорошо работают со многими элементами, в то время как простые алгоритмы больше подходят для небольших объемов данных — настройка, время инициализации и постоянные факторы более сложного алгоритма могут перевесить выгоду, и, следовательно, алгоритм гибридный или адаптивный Алгоритм может быть быстрее, чем любой отдельный алгоритм. Профилировщик производительности можно использовать для сужения решений о том, какая функциональность соответствует каким условиям. [4]
В некоторых случаях добавление дополнительной памяти может помочь ускорить работу программы. Например, программа фильтрации обычно считывает каждую строку, фильтрует и немедленно выводит эту строку. При этом используется достаточно памяти только для одной строки, но производительность обычно низкая из-за задержки каждого чтения с диска. Кэширование результата столь же эффективно, хотя и требует большего использования памяти.
Когда оптимизировать
[ редактировать ]Оптимизация может снизить читаемость и добавить код, который используется только для повышения производительности . Это может усложнить программы или системы, усложнив их обслуживание и отладку. В результате оптимизация или настройка производительности часто выполняется в конце этапа разработки .
Дональд Кнут сделал следующие два заявления по поводу оптимизации:
«Мы должны забыть о небольшой эффективности, скажем, в 97% случаев: преждевременная оптимизация — корень всех зол. Однако мы не должны упускать наши возможности в этих критических 3%» [5]
он также приписал эту цитату Тони Хоару : ( Несколько лет спустя [6] хотя это могло быть ошибкой, поскольку Хоар отрицает, что придумал эту фразу. [7] )
«В устоявшихся инженерных дисциплинах легко достижимое улучшение на 12% никогда не считается незначительным, и я считаю, что та же точка зрения должна преобладать в разработке программного обеспечения». [5]
«Преждевременная оптимизация» — это фраза, используемая для описания ситуации, когда программист позволяет соображениям производительности влиять на дизайн фрагмента кода. Это может привести к тому, что дизайн будет не таким чистым, каким мог бы быть, или неверным кодом, поскольку код усложняется оптимизацией и программист отвлекается на оптимизацию.
При принятии решения об оптимизации конкретной части программы всегда следует учитывать закон Амдала : влияние на программу в целом во многом зависит от того, сколько времени фактически затрачивается на эту конкретную часть, что не всегда ясно при взгляде на код. без анализа производительности .
Поэтому лучший подход — сначала спроектировать код на основе проекта, а затем профилировать / тестировать полученный код, чтобы увидеть, какие части следует оптимизировать. Простой и элегантный дизайн на этом этапе зачастую легче оптимизировать, а профилирование может выявить неожиданные проблемы с производительностью, которые нельзя было бы устранить преждевременной оптимизацией.
На практике при первом проектировании программного обеспечения часто необходимо учитывать цели производительности, но программист балансирует цели проектирования и оптимизации.
Современные компиляторы и операционные системы настолько эффективны, что запланированное увеличение производительности часто не реализуется. Например, кэширование данных на уровне приложения, которые затем снова кэшируются на уровне операционной системы, не приводит к улучшению выполнения. Тем не менее, это редкий случай, когда программист удаляет неудачные оптимизации из рабочего кода. Верно также и то, что достижения в области аппаратного обеспечения чаще всего устраняют любые потенциальные улучшения, однако скрывающий код будет сохраняться в будущем еще долго после того, как его цель будет отменена.
Макросы
[ редактировать ]Оптимизация при разработке кода с использованием макросов принимает разные формы на разных языках.
В некоторых процедурных языках, таких как C и C++ , макросы реализуются с использованием замены токенов. В настоящее время встроенные функции могут использоваться как типобезопасная во многих случаях альтернатива. В обоих случаях тело встроенной функции может затем подвергаться дальнейшей оптимизации компилятором во время компиляции, включая свертывание констант , что может перенести некоторые вычисления на время компиляции.
Во многих языках функционального программирования макросы реализуются с использованием замены деревьев синтаксического анализа/абстрактных синтаксических деревьев во время анализа, что, как утверждается, делает их более безопасными в использовании. Поскольку во многих случаях используется интерпретация, это один из способов гарантировать, что такие вычисления выполняются только во время синтаксического анализа, а иногда и единственный способ.
Лисп создал этот стиль макросов, [ нужна ссылка ] и такие макросы часто называют «lisp-подобными макросами». Аналогичного эффекта можно добиться, используя метапрограммирование шаблонов в C++ .
В обоих случаях работа переносится на время компиляции. Разница между макросами C, с одной стороны, и макросами, подобными Lisp, и C++, метапрограммированием шаблонов с другой, заключается в том, что последние инструменты позволяют выполнять произвольные вычисления во время компиляции/анализа, в то время как расширение макросов C не выполняет никаких действий. вычислений и полагается на способность оптимизатора выполнить их. Кроме того, макросы C не поддерживают напрямую рекурсию или итерацию , поэтому не являются полными по Тьюрингу .
Однако, как и в случае любой оптимизации, часто бывает трудно предсказать, где такие инструменты окажут наибольшее влияние, прежде чем проект будет завершен.
Автоматическая и ручная оптимизация
[ редактировать ]См. также Категория:Оптимизация компилятора.
Оптимизация может быть автоматизирована компиляторами или выполнена программистами. Прирост обычно ограничен при локальной оптимизации и больше при глобальной оптимизации. Обычно самая мощная оптимизация — это поиск лучшего алгоритма .
Оптимизацией всей системы обычно занимаются программисты, поскольку она слишком сложна для автоматических оптимизаторов. В этой ситуации программисты или системные администраторы явно меняют код, чтобы вся система работала лучше. Хотя это может обеспечить более высокую эффективность, это гораздо дороже, чем автоматическая оптимизация. Поскольку на производительность программы влияет множество параметров, пространство для оптимизации программы велико. Метаэвристика и машинное обучение используются для решения сложных задач по оптимизации программ. [8]
Используйте профилировщик (или анализатор производительности ), чтобы найти разделы программы, которые забирают больше всего ресурсов — узкое место . Программисты иногда полагают, что имеют четкое представление о том, где находится узкое место, но интуиция часто ошибается. [ нужна ссылка ] Оптимизация неважного фрагмента кода обычно мало что дает для повышения общей производительности.
Когда узкое место локализовано, оптимизация обычно начинается с переосмысления алгоритма, используемого в программе. Чаще всего конкретный алгоритм можно специально адаптировать для конкретной задачи, обеспечивая более высокую производительность, чем общий алгоритм. Например, задача сортировки огромного списка элементов обычно выполняется с помощью процедуры быстрой сортировки , которая является одним из наиболее эффективных универсальных алгоритмов. Но если некоторые характеристики элементов можно использовать (например, они уже расположены в определенном порядке), можно использовать другой метод или даже специальную процедуру сортировки.
После того, как программист будет достаточно уверен, что выбран лучший алгоритм, можно приступить к оптимизации кода. Циклы можно развертывать (для снижения накладных расходов на циклы, хотя это часто может привести к снижению скорости, если перегружает кэш ЦП ), можно использовать как можно меньшие типы данных, можно использовать целочисленную арифметику вместо чисел с плавающей запятой и т. д. . (Об этих и других методах см. в статье об эффективности алгоритмов .)
Узкие места производительности могут быть связаны с ограничениями языка, а не с алгоритмами или структурами данных, используемыми в программе. Иногда критически важную часть программы можно переписать на другом языке программирования , который обеспечивает более прямой доступ к базовой машине. Например, для очень высокого уровня, языков таких как Python, модули написаны на C. для большей скорости Программы, уже написанные на C, могут иметь модули, написанные на ассемблере . Программы, написанные на D, могут использовать встроенный ассемблер .
Переписывание разделов «окупается» в таких обстоятельствах из-за общего « эмпирического правила », известного как закон 90/10, который гласит, что 90% времени тратится на 10% кода, и только 10% времени в остальных 90% кода. Таким образом, вложение интеллектуальных усилий в оптимизацию лишь небольшой части программы может оказать огромное влияние на общую скорость — если можно найти правильную часть(и).
Ручная оптимизация иногда имеет побочный эффект — ухудшение читабельности. Таким образом, оптимизация кода должна быть тщательно документирована (желательно с использованием встроенных комментариев) и оценена ее влияние на будущую разработку.
Программа, выполняющая автоматическую оптимизацию, называется оптимизатором . Большинство оптимизаторов встроены в компиляторы и работают во время компиляции. Оптимизаторы часто могут адаптировать сгенерированный код к конкретным процессорам.
Сегодня автоматизированная оптимизация почти исключительно ограничивается оптимизацией компилятора . Однако, поскольку оптимизация компилятора обычно ограничивается фиксированным набором довольно общих оптимизаций, существует значительная потребность в оптимизаторах, которые могут принимать описания проблем и оптимизации для конкретного языка, что позволяет инженеру указывать собственные оптимизации. Инструменты, принимающие описания оптимизаций, называются системами преобразования программ и начинают применяться к реальным программным системам, таким как C++.
Некоторые языки высокого уровня ( Eiffel , Esterel ) оптимизируют свои программы, используя промежуточный язык .
Грид-вычисления или распределенные вычисления направлены на оптимизацию всей системы путем перемещения задач с компьютеров с высокой нагрузкой на компьютеры с временем простоя.
Время, потраченное на оптимизацию
[ редактировать ]Иногда время, затраченное на оптимизацию, само по себе может быть проблемой.
Оптимизация существующего кода обычно не добавляет новых функций и, что еще хуже, может добавить новые ошибки в ранее работающий код (как и любое изменение). Поскольку код, оптимизированный вручную, иногда может иметь меньшую «читабельность», чем неоптимизированный код, оптимизация также может повлиять на его удобство сопровождения. За оптимизацию приходится платить, и важно быть уверенным, что инвестиции окупаются.
Автоматический оптимизатор (или оптимизирующий компилятор , программа, выполняющая оптимизацию кода) может сам быть оптимизирован либо для дальнейшего повышения эффективности целевых программ, либо для ускорения собственной работы. Компиляция, выполняемая с «включенной» оптимизацией, обычно занимает больше времени, хотя обычно это проблема только в том случае, когда программы достаточно большие.
В частности, для JIT-компиляторов производительность компонента компиляции во время выполнения , выполняющегося вместе с целевым кодом, является ключом к повышению общей скорости выполнения.
Ссылки
[ редактировать ]- ^ Роберт Седжвик , Алгоритмы , 1984, с. 84.
- ^ Адевуми, Тосин П. (01 августа 2018 г.). «Конструкция программы внутреннего цикла: более быстрый способ выполнения программы» . Открытая информатика . 8 (1): 115–122. дои : 10.1515/comp-2018-0004 .
- ^ Уэскотт, Боб (2013). Книга о производительности каждого компьютера, глава 3: Полезные законы . Создать пространство . ISBN 978-1482657753 .
- ^ «Профилирование производительности с фокусом» . Проверено 15 августа 2017 г.
- ^ Перейти обратно: а б Кнут, Дональд (декабрь 1974 г.). «Структурное программирование с переходом к операторам». Обзоры вычислительной техники ACM . 6 (4): 268. CiteSeerX 10.1.1.103.6084 . дои : 10.1145/356635.356640 . S2CID 207630080 .
- ^ Ошибки TeX , в журнале «Программное обеспечение — практика и опыт» , том 19, выпуск 7 (июль 1989 г.), стр. 607–685, перепечатано в его книге «Грамотное программирование» (стр. 276).
- ^ «Преждевременная оптимизация — корень всех зол» . hans.gerwitz.com . Проверено 18 декабря 2020 г.
Хоар, однако, не утверждал этого, когда я спрашивал его в январе 2004 года.
- ^ Мемети, Суэйб; Планана, Сабри; Бинотто, Алесио; Колодзей, Иоанна; Брандич, Ивона (26 апреля 2018 г.). «Использование метаэвристики и машинного обучения для оптимизации программного обеспечения параллельных вычислительных систем: систематический обзор литературы». Вычисление . 101 (8). Спрингер Вена: 893–936. arXiv : 1801.09444 . Бибкод : 2018arXiv180109444M . дои : 10.1007/s00607-018-0614-9 . S2CID 13868111 .
Дальнейшее чтение
[ редактировать ]- Джон Бентли : Написание эффективных программ , ISBN 0-13-970251-2 .
- Дональд Кнут : Искусство компьютерного программирования
- Как написать быстрый числовой код: небольшое введение
- «Что каждый программист должен знать о памяти» Ульриха Дреппера - объясняет структуру современных подсистем памяти и предлагает, как их эффективно использовать.
- «Анализ и оптимизация многоядерной производительности Linux в двух словах» , слайды презентации Филипа Муччи
- Оптимизация программирования Пола Се
- Написание эффективных программ («Правила Бентли») Джона Бентли .
- «Антипаттерны производительности», Барт Смаалдерс