Подтвердить что ты не робот

Оптимизация! - Что это? Как это делается?

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

Ухаживать за общей идеей? Кроме того, рекомендую некоторые материалы для чтения и, действительно, все, что вы хотите сказать по этому поводу. Не стесняйтесь говорить и проповедовать.

4b9b3361

Ответ 1

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

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

Можно оптимизировать для:

  • Потребление памяти. Уменьшите размер времени выполнения программы или алгоритма.

  • Потребление процессора. Сделать алгоритм вычислительно менее интенсивным.

  • Время на стене. Сделайте все, что нужно, чтобы сделать что-то быстрее

  • Читаемость. Вместо того чтобы сделать ваше приложение лучше для компьютера, вы можете облегчить его чтение человеком.

Некоторые общие (и слишком обобщенные) методы оптимизации кода включают в себя:

  • Измените алгоритм для улучшения характеристик производительности. Если у вас есть алгоритм, который принимает O (n ^ 2) время или пробел, попробуйте заменить этот алгоритм на тот, который принимает O (n * log n).

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

  • Чтобы уменьшить потребление ЦП, кешируйте как можно больше промежуточных результатов. Например, если вам нужно рассчитать стандартное отклонение набора данных, сохраните этот единственный числовой результат, а затем перейдя через набор каждый раз, когда вам нужно знать std dev.

Ответ 2

В основном я пишу без практических советов.

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

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

И, наконец, несколько слов об оптимизации. Большинство обсуждений по оптимизации, которые я вижу, сосредоточены на материалах, которые любой компилятор будет оптимизировать для вас бесплатно. По моему опыту, наибольший источник выигрышей для "высоко оптимизированного кода" полностью лежит в другом месте: доступ к памяти. На современных архитектурах процессор работает на холостом ходу большую часть времени, ожидая, что память будет подана в его конвейеры. Между промахами кеша L1 и L2 TLB промахивается, NUMA cross- node и даже GPF, который должен извлекать страницу с диска, шаблон доступа к памяти для современного приложения является самым большим важную оптимизацию можно сделать. Я немного преувеличиваю, конечно, будут встречные примеры рабочих нагрузок, которые не будут полезны для локализации доступа к памяти этим методам. Но большинство приложений будет. Чтобы быть конкретным, то, что означают эти методы, просто: сгруппируйте свои данные в памяти, чтобы один процессор мог работать с ограниченным диапазоном памяти, содержащим все, что ему нужно, без дорогостоящей ссылки на память за пределами ваших линий кэша или на вашу текущую страницу. На практике это может означать нечто такое же простое, как доступ к массиву по строкам, а не по столбцам.

Я бы порекомендовал вам прочитать документ Alpha-Sort, представленный на конференции VLDB в 1995 году. В этом документе описывается, как разработаны алгоритмы кэширования специально для современных архитектур процессоров, могут вывести из воды старые предыдущие тесты:

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

Ответ 3

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

Подумайте о таких вещах, как этот кусок кода:

a=(b+c)*3-(b+c)

который переводится в

        -
     *     + 
    + 3   b c
   b c 

Для анализатора было бы очевидно, что + node с двумя его потомками идентичны, поэтому они будут объединены в временную переменную t, и дерево будет переписано:

        -
     *     t 
    t 3   

Теперь еще лучший анализатор увидит, что, поскольку t является целым числом, дерево может быть дополнительно упрощено до:

        *
     t     2 

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

int t=b+c;
a=t*2;

с t, помеченным как регистровая переменная, что именно то, что было бы записано для сборки.

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

Ответ 4

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

В приложении, над которым я работал, было несколько структур данных LinkedList, которые использовались для хранения различных экземпляров foo.

Когда приложение использовалось, очень часто проверялось, содержит ли объект LinkedListed объект X. По мере того, как количество X начинает расти, я заметил, что приложение работает медленнее, чем должно было быть.

Я запустил профайлер и понял, что каждый вызов myList.Contains(x) имеет O (N), потому что список должен перебирать каждый элемент, который он содержит, до тех пор, пока он не достигнет конца или не найдет совпадение. Это определенно неэффективно.

Итак, что я сделал, чтобы оптимизировать этот код? Я переключил большую часть ссылок на LinkedList на HashSets, которые могут выполнять вызов ".Contains(X)" в O (1) - намного лучше.

Ответ 5

Это хороший вопрос.

Обычно наилучшей практикой является 1) просто напишите код, чтобы сделать то, что вам нужно, чтобы сделать это, 2) затем справиться с производительностью, но только если это проблема. Если программа "достаточно быстро", это не проблема.

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

Не угадайте, что исправить; диагностировать, что делает программа. Все это знают, но в основном они это делают. Естественно сказать: "Может быть, проблема X, Y или Z", но только начинающий действует на догадки. Про говорит: "Но я, вероятно, ошибаюсь".

Существуют различные способы диагностики проблем с производительностью.

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

Другим является получение инструмента профилирования, а, как говорят другие, измерение, измерение, измерение.

Лично меня не интересует измерение. Я думаю, что это нечеткий микроскоп с целью выявления проблем с производительностью. Я предпочитаю этот метод, а это пример его использования.

Удачи.

ДОБАВЛЕН: Я думаю, вы обнаружите, что если вы пройдете это упражнение несколько раз, вы узнаете, какие методы кодирования, как правило, приводят к проблемам с производительностью, и вы инстинктивно избегаете их. (Это немного отличается от "преждевременной оптимизации", которая вначале предполагает, что вы должны быть обеспокоены производительностью. Фактически, вы, вероятно, узнаете, если вы еще не знаете, что преждевременная озабоченность по поводу производительности может привести к тому, что очень проблема, которую она пытается избежать.)

Ответ 6

Оптимизация программы означает: ускорить выполнение

Единственный способ сделать программу быстрее - сделать меньше:

  • найти алгоритм, который использует меньше операций (например, N log N вместо N ^ 2)
  • избегать медленных компонентов вашего устройства (сохранять объекты в кеше вместо основной памяти или в основной памяти вместо диска); сокращение потребления памяти почти всегда помогает!

Дополнительные правила:

  • При поиске возможностей оптимизации придерживайтесь правила 80-20: 20% типичных учетных записей программных кодов составляют 80% времени выполнения.
  • Измерьте время до и после каждой попытки оптимизации; достаточно часто оптимизаций нет.
  • Оптимизировать только после правильной работы программы!

Кроме того, есть способы сделать программу более быстрой:

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

Ответ 7

Однако, будучи самонаучным, новым программистом, я никогда не понимал, что именно люди имеют в виду, говоря о таких вещах.

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

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

  • У них есть данные
  • Они выбирают алгоритм, который, как они знают, быстрее математически.
  • Они выбирают структуру данных, которая, как они знают, быстрее математически.

Ответ 8

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

Ответ 9

Я предлагаю вам сначала прочитать немного теории (из книг или Google для слайдов):

  • Структуры данных и алгоритмы - что такое O() нотация, как ее вычислять, какие datastructures и алгоритмы могут быть использованы для снижения O-сложности
    Книга: Введение в алгоритмы Томаса Х. Кормена, Чарльза Е. Лейзерсона и Рональда Л. Ривеста

  • Компиляторы и сборка - как код преобразуется в машинные инструкции

  • Архитектура компьютера - как процессор, оперативная память, кеш, предсказания ветвей, выход из строя... работа

  • Операционные системы - режим ядра, пользовательский режим, процессы/потоки планирования, мьютексы, семафоры, очереди сообщений

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

Примечание. Я викиировал это, чтобы люди могли добавлять рекомендации по книге.

Ответ 10

Я собираюсь с идеей, что оптимизация кода - это получение тех же результатов за меньшее время. И полностью оптимизированный только означает, что у них кончились идеи, чтобы сделать это быстрее. Я бросаю большие ведра презрения по поводу претензий на "полностью оптимизированный" код! Нет такой вещи.

Итак, вы хотите, чтобы ваше приложение/программа/модуль работали быстрее? Первое, что нужно сделать (как упоминалось ранее) - это измерение, известное также как профилирование. Не угадайте, где оптимизировать. Ты не такой умный, и ты ошибешься. Мои догадки ошибочны все время, и большая часть моего года тратится на профилирование и оптимизацию. Поэтому заставьте компьютер сделать это за вас. Для ПК VTune - отличный профайлер. Я думаю, VS2008 имеет встроенный профилировщик, но я не изучал его. В противном случае измерьте функции и большие куски кода с счетчиками производительности. Вы найдете пример кода для использования счетчиков производительности в MSDN.

Итак, где ваши циклы? Вероятно, вы ожидаете, что данные поступают из основной памяти. Идите читать в кешках L1 и L2. Понимание того, как работает кеш, - половина битвы. Подсказка: используйте плотные, компактные структуры, которые будут больше вписываться в строку кеша.

Оптимизация - это очень весело. И это никогда не заканчивается:)

Отличная книга по оптимизации - напишите Великий код: Понимание машины по Randall Hyde.

Ответ 11

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