Стратегии оптимизации производительности последней инстанции [закрыто]

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

допустим:

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

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

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

Я добавлю ответ с моим собственные первоначальные предложения и с нетерпением ждем, что еще может придумать сообщество переполнения стека.

30 ответов


хорошо, вы определяете проблему там, где, казалось бы, нет места для улучшения. По моему опыту, это большая редкость. Я попытался объяснить это в статье доктора Доббса в ноябре 93-го, начав с хорошо продуманной нетривиальной программы без очевидных потерь и проведя ее через ряд оптимизаций, пока время настенных часов не сократилось с 48 секунд до 1,1 секунды, а размер исходного кода был уменьшен в 4 раза. Мой диагностический инструмент этот. Последовательность изменений была такова:--9-->

  • первой обнаруженной проблемой было использование кластеров списков (теперь называемых "итераторами" и "контейнерными классами"), которые составляли более половины времени. Они были заменены довольно простым кодом, в результате чего время сократилось до 20 секунд.

  • теперь самый большой time-taker больше список здания. В процентах, это было не так много раньше, но теперь это потому, что большая проблема была удалена. Я найдите способ ускорить его, и время упадет до 17 сек.

  • теперь труднее найти очевидных виновников, но есть несколько меньших, с которыми я могу что-то сделать, и время падает до 13 сек.

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

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

  • что редизайн сделан, сокращается исходный код в 4 раза, а время сокращается до 10 секунд.

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

  • больше диагноз показывает, что он тратит время в управлении очередями. В-подкладка эти уменьшает время до 7 секунд.

  • теперь большой time-taker диагностическое печатание я имел делал. Вровень это - 4 секунды.

  • теперь самые большие тайм-тейкеры-это звонки на Танос и свободный. Объекты переработки - 2,6 секунды.

  • продолжая образец, я все еще нахожу операции, которые не являются строго необходимыми-1,1 секунды.

общий коэффициент ускорения: 43.6

теперь нет двух одинаковых программ, но в не игрушечном программном обеспечении я всегда видел такую прогрессию. Сначала вы получаете легкий материал, а затем более сложный, пока не дойдете до точки убывающей отдачи. Тогда понимание, которое вы получите, может привести к перепроектированию, запуску нового раунда ускорений, пока вы снова не нажмете уменьшающуюся отдачу. Теперь это точка, в которой может иметь смысл задаться вопросом, является ли ++i или i++ или for(;;) или while(1) быстрее: виды вопросов, которые я вижу так часто на SO.

P. S. Это может быть интересно, почему я не использовать профайлер. Ответ таков: почти каждая из этих "проблем"была сайтом вызова функций, который стек образцов точно. Профилировщики даже сегодня едва приходят к мысли, что операторы и инструкции вызова более важны для поиска и легче исправить, чем целые функции. На самом деле я построил для этого профайлер, но для реальной и грязной близости с тем, что делает код, нет замены для получения ваших пальцев прямо в нем. Это не проблема, что количество образцов невелико, потому что ни одна из обнаруженных проблем не настолько мала, что их легко пропустить.

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

 /* IF ALL TASKS DONE, SEND ITC_ACKOP, AND DELETE OP */
if (ptop->current_task >= ILST_LENGTH(ptop->tasklist){
. . .
/* FOR EACH OPERATION REQUEST */
for ( ptop = ILST_FIRST(oplist); ptop != NULL; ptop = ILST_NEXT(oplist, ptop)){
. . .
/* GET CURRENT TASK */
ptask = ILST_NTH(ptop->tasklist, ptop->current_task)

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

вот вторая проблема, в двух отдельных строках:

 /* ADD TASK TO TASK LIST */ 
ILST_APPEND(ptop->tasklist, ptask)
. . .
/* ADD TRANSACTION TO TRANSACTION QUEUE */
ILST_APPEND(trnque, ptrn)

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

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

ССЫЛКА ДОБАВЛЕНА: Этот исходный код, как оригинальный, так и переработанный, можно найти вwww.ddj.com, за 1993 год, в файле 9311.zip, файлы slug.АСК и слизняк.застежка-молния.

изменить 2011/11/26: Сейчас проект sourceforge содержит исходный код в Visual C++ и пошаговое описание того, как он был настроен. Он проходит только первую половину сценария, описанного выше, и он не следует точно той же последовательности, но все равно получает ускорение на 2-3 порядка.


предложения:

  • предварительно вычислить, а не пересчитать: любые циклы или повторяющиеся вызовы, содержащие вычисления с относительно ограниченным диапазоном входных данных, рассмотрите возможность поиска (массив или словарь), содержащий результат этого вычисления для всех значений в допустимом диапазоне входных данных. Вместо этого используйте простой поиск внутри алгоритма.
    вниз-стороны: если некоторые из предварительно вычисленных значений фактически используются, это может сделать хуже того, поиск может занять значительную память.
  • не используйте методы, библиотека: большинство библиотек должны быть написаны для правильной работы в широком диапазоне сценариев и выполнения нулевых проверок параметров и т. д. При повторной реализации метода вы можете вычеркнуть много логики, которая не применяется в конкретных обстоятельствах, которые вы используете.
    вниз-стороны: писать дополнительный код значит больше поверхностной области для жуки.
  • используйте методы библиотеки: чтобы противоречить себе, языковые библиотеки пишутся людьми, которые намного умнее вас или меня; скорее всего, они сделали это лучше и быстрее. Не реализуйте его самостоятельно, если вы не можете сделать это быстрее (т. е. всегда измеряйте!)
  • чит: в некоторых случаях, хотя точный расчет может существовать для вашей проблемы, вам может не понадобиться "точный", иногда приближение может быть "достаточно хорошим" и много быстрее в интернет. Спросите себя, действительно ли имеет значение, если ответ отсутствует на 1%? 5%? даже 10%?
    вниз-стороны: хорошо... ответ не будет точным.

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

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

несколько примеров:

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

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


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

  • кэш-промахов. Кэш данных является источником # 1 киосков в большинстве программ. Улучшите скорость попадания в кэш, реорганизовав структуры данных, чтобы иметь лучшую локальность; упакуйте структуры и числовые типы, чтобы исключить потерянные байты (и, следовательно, потерянные выборки кэша); данные предварительной выборки, где это возможно, чтобы уменьшить стойло.
  • Load-hit-магазины. Предположения компилятора о сглаживании указателей и случаях, когда данные перемещаются между отключенными наборами регистров через память, могут вызвать определенное патологическое поведение, которое приводит к очистке всего конвейера ЦП при загрузке op. Найдите места, где поплавки, векторы и Инты бросаются друг к другу и устраняют их. Использовать __restrict либерально пообещать компилятору об псевдонимах.
  • платформах операции. Наиболее процессоры имеют некоторые операции, которые не могут быть конвейеризованы, но вместо этого запускают крошечную подпрограмму, хранящуюся в ПЗУ. Примерами на PowerPC являются целочисленное умножение, деление и сдвиг на переменную величину. Проблема в том, что во время выполнения этой операции весь конвейер останавливается. Попробуйте исключить использование этих операций или, по крайней мере, разбить их на составляющие конвейерные операции, чтобы вы могли получить преимущество superscalar dispatch на любой остальной части вашей программы делающий.
  • филиала mispredicts. Они тоже опустошают трубопровод. Найдите случаи, когда процессор тратит много времени на заправку трубы после ветви, и используйте ветвь, намекающую, если она доступна, чтобы заставить ее предсказывать правильно чаще. Или еще лучше, замените ветви условными-перемещениями, где это возможно,особенно после операций с плавающей запятой, потому что их труба обычно глубже и чтение флагов условий после fcmp может вызвать киоск.
  • последовательные операции с плавающей запятой. Сделайте эти SIMD.

и еще одна вещь, которую я люблю делать:

  • установить компилятор для вывода списков сборки и посмотрите, что он излучает для функций hotspot в вашем коде. Все эти умные оптимизации, которые "хороший компилятор должен быть в состоянии сделать для вас автоматически"? Скорее всего, ваш фактический компилятор их не делает. Я видел, как GCC испускает действительно WTF код.

бросьте больше оборудования на него!


дополнительные пожелания:

  • Избегайте Ввода-Вывода: любой I /O (диск, сеть, порты, etc.) есть всегда будет намного медленнее, чем любой код, который выполнение вычислений, поэтому избавьтесь от любого ввода-вывода, который вы делаете не совсем нужно.

  • переместить ввод-вывод вперед: загрузить все данные, которые вы собираетесь нужно для расчета заранее, чтобы вы этого не сделали повторные ожидания ввода-вывода в ядре критического алгоритм (и, возможно, как результат повторного поиска диска, когда загрузка всех данных одним ударом может избежать поиска).

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

  • Продетый Нитку I / O: для тех, кто достаточно смелый, объединить ' I / O Up-front " или "задержка ввода-вывода" с фактическим расчетом по перемещение загрузки в параллель нить, так что пока вы загружаете больше данных, на которых вы можете работать над вычислением данные у вас уже есть, или пока вы вычисляете следующий пакет данных, вы можете одновременно записать результаты из последней партии.


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

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

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

избегайте использования коррелированных подзапросов. Используйте соединения (включая соединения с производными таблицами, где это возможно) (я знаю, что это верно для Microsoft SQL Server, но проверьте совет при использовании другого бэкэнда).

индекс, индекс, индекс. И получите обновленную статистику, если это применимо к вашему база данных.

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

Используйте правильные типы данных. Быстрее выполнить математику даты в поле даты, чем пытаться преобразовать строковый тип данных в тип даты, а затем выполнить вычисление.

никогда не ставил петли в триггер!

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

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

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

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


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

Это те эффекты, которые вам нужно управлять. Иногда через микро-управление кодом, но иногда через тщательное рассмотрение и рефакторинг.

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

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

первая проблема конкретно связана с созданием вашего шаблоны доступа к данным более регулярны, что позволяет аппаратному prefetcher работать эффективно. Избегайте динамического выделения памяти, которое распространяет объекты данных в памяти. Используйте линейные контейнеры вместо связанных списков, хэшей и деревьев.

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

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


  • на каком оборудовании вы работаете? Можете ли вы использовать оптимизацию для конкретной платформы (например, векторизацию)?
  • может вам лучше компилятора? Е. Г. переход от ССЗ в Intel?
  • можете ли вы заставить свой алгоритм работать параллельно?
  • можно ли уменьшить пропуски кэша путем реорганизации данных?
  • можете ли вы отключить утверждения?
  • микро-оптимизации для компилятора и платформы. В стиле " в if / else, поставьте наиболее распространенное утверждение первый"

  • встроенные процедуры (исключить вызов/возврат и нажатие параметров)
  • попробуйте устранить тесты / переключатели с помощью таблицы look ups (если они быстрее)
  • развернуть петли (устройство Даффа) до точки, где они просто вписываются в кэш процессора
  • локализовать доступ к памяти, чтобы не взорвать свой кэш
  • локализовать связанные вычисления, если оптимизатор еще не делает этого
  • исключить инварианты цикла, если оптимизатор еще не делает это

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

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

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


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

узнайте, что занимает наибольшее количество времени в первую очередь, и понять, почему.

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

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

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

HPH, asoudmove.


  • когда Вы дойдете до того, что используете эффективные алгоритмы, вопрос в том, что вам нужно больше скорость или память. Используйте кэширование для "оплаты" в памяти для большей скорости или используйте вычисления для уменьшения объема памяти.
  • Если возможно (и более рентабельно) заменить оборудование - быстрее CPU, больше памяти или HD может решить проблему быстрее, чем пытаться ее кодировать.
  • использовать распараллеливание Если возможно - выполнить часть кода в нескольких потоках.
  • Используйте правильный инструмент для работы. некоторые языки программирования создают более эффективный код, используя управляемый код (например, Java / .NET), ускоряя разработку, но собственные языки программирования создают более быстрый код.
  • микро-оптимизации. Только были применимы вы можете использовать оптимизированную сборку для ускорения небольших фрагментов кода, используя оптимизацию SSE / vector в нужных местах, может значительно увеличиться спектакль.

Разделяй и властвуй

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


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

  • ...если это память-найдите одну из книг, написанных давным-давно кнутом, одну из серии "искусство компьютерного программирования". Скорее всего, речь идет о сортировке и поиске - если моя память ошибается, тогда вам придется узнать, в какой он говорит о том, как обращаться с медленной лентой хранение данных. Мысленно преобразите его памяти/лента пара в вашу пару кэша / основной памяти (или в пару кэша L1/L2) соответственно. Изучите все трюки, которые он описывает - если вы не найдете что-то, что решает вашу проблему, то наймите профессионального компьютерщика для проведения профессионального исследования. Если ваша проблема с памятью случайно связана с FFT (кэш пропускает битовые обратные индексы при выполнении бабочек radix-2), то не нанимайте ученого-вместо этого вручную оптимизируйте проходы один за другим, пока вы не выиграете или не попадете в тупик. Вы упомянули выжать до последних нескольких процентов верно? Если это несколько действительно, Вы, скорее всего, выиграете.

  • ...если это процессор-переключитесь на язык ассемблера. Исследование спецификации процессора -что берет клещами, VLIW, SIMD. Вызовы функций, скорее всего, являются сменными ТИК-едоками. Изучите преобразования цикла-конвейер, разверните. Множители и деления могут быть заменяемый / интерполированный с битовыми сдвигами (умножение на малые целые числа может быть заменено добавлениями). Попробуйте трюки с более короткими данными - если Вам ПОВЕЗЕТ, одна инструкция с 64 битами может оказаться заменяемой с двумя на 32 или даже 4 на 16 или 8 на 8 битах. Попробуйте также больше данные-например, ваши вычисления float могут оказаться медленнее, чем двойные на конкретном процессоре. Если у вас есть тригонометрические вещи, бороться с ним с заранее рассчитанными таблицами; также имейте в виду, что синус малое значение может быть заменено этим значением, если потеря точности находится в допустимых пределах.

  • ...если это сеть-подумайте о сжатии данных, которые вы передаете. Замените передачу XML двоичным кодом. Протокол исследования. Попробуйте UDP вместо TCP, если вы можете как-то справиться с потерей данных.

  • ...если это база данных, ну, перейдите на любой форум базы данных и попросите совета. В памяти data-grid, оптимизируя план запроса etc etc так далее.

HTH:)


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

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

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


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

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


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


последние несколько % - это очень зависимая от процессора и приложений вещь....

  • архитектуры кэша отличаются, некоторые чипы имеют ОЗУ на чипе вы можете отображать непосредственно, ARM (иногда) имеют вектор юнит, SH4-полезный матричный код. Есть ли GPU - может быть, шейдер-это путь. TMS320очень чувствительный к ветвям внутри петель (так отдельные петли и условия перемещения наружу, если это возможно).

список продолжается.... Но эти некоторые вещи действительно последнее средство...

построить для x86 и запустить отчет/Cachegrind против кода для правильного профилирования производительности. Или Texas Instruments CCStudio имеет сладкий профайлера. Тогда ты действительно будешь знать, где заострять внимание...


Did you know that a CAT6 cable is capable of 10x better shielding off extrenal inteferences than a default Cat5e UTP cable?

для любых не-автономных проектов, имея лучшее программное обеспечение и лучшее оборудование, если ваш сквозной выход слаб, то эта тонкая линия будет сжимать данные и давать вам задержки, хотя и в миллисекундах... но если вы говорите о последних каплях, это несколько капель, полученных 24/7 для любого отправленного или полученного packge.


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

  • мера: начните с понимания базовой емкости и топологии сети. Поговорите с соответствующими сетевыми людьми в бизнесе и используйте основные инструменты, такие как ping и traceroute для установления (как минимум) задержки сети из каждого местоположения клиента в течение типичных периодов работы. Затем выполните точные измерения времени конкретных функций конечного пользователя, отображающих проблемные симптомы. Запишите все эти измерения вместе с их местоположением, датами и временем. Рассмотрите возможность создания в клиентском приложении функциональности "тестирование производительности сети" для конечных пользователей, позволяющей пользователям участвовать в процессе улучшения; дать им такой тег огромный психологическое воздействие, когда вы имеете дело с пользователями разочарование от плохо функционирующей системы.

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

  • кэш: ищите случаи, когда статические или редко измененные данные передаются повторно, и рассмотрите соответствующую стратегию кэширования. Типичные примеры включают значения "pick list" или другие "ссылочные сущности", которые могут быть удивительно большими в некоторых бизнес-приложениях. Во многих случаях пользователи могут согласиться с тем, что они должны перезапустить или обновить приложение для обновления редко обновляемых данных, особенно если это может сократить значительное время от отображения часто используемых элементов пользовательского интерфейса. Убедитесь, что вы понимаете реальное поведение уже развернутых элементов кэширования - многие распространенные методы кэширования (например, HTTP ETag) по-прежнему требуют сетевого обхода для обеспечения согласованности, и там, где сетевая задержка стоит дорого, вы можете избежать ее вообще с другим подходом кэширования.

  • Parallelise: ищите последовательные транзакции, которые логически не должны выдаваться строго последовательно, и переработайте систему, чтобы выпускать их параллельно. Я имел дело с одним случаем, когда сквозной запрос имел неотъемлемую сетевую задержку ~2s, что не было проблемой для одной транзакции, но когда 6 последовательных 2S туда и обратно были необходимы, прежде чем пользователь восстановил контроль над клиентским приложением, это стало огромным источником разочарования. Открытие того, что эти транзакции были фактически независимыми, позволило им выполняться параллельно, сократив задержку конечного пользователя до очень близкой к стоимости одной поездки туда и обратно.

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

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

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

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


не так глубоко или сложно, как предыдущие ответы, но здесь идет: (это более начинающий/средний уровень)

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

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

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

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

Ну, это и "показать код на SO и попросить совета по оптимизации для этого конкретного фрагмента кода".


Если лучше аппаратный вариант, то однозначно идти. В противном случае

  • проверьте, что вы используете лучшие параметры компилятора и компоновщика.
  • Если hotspot routine в другой библиотеке для частого вызывающего абонента, подумайте о перемещении или клонировании его в модуль вызывающих абонентов. Устраняет некоторые из накладных расходов вызова и может улучшить хиты кэша (cf, как AIX связывает strcpy() статически в отдельно связанные общие объекты). Это, конечно, может также уменьшить кеш-хиты, поэтому одна мера.
  • посмотрите, есть ли возможность использовать специализированную версию процедуры hotspot. Недостатком является более чем одна версия для поддержания.
  • посмотрите на ассемблер. Если вы считаете, что это может быть лучше, подумайте, почему компилятор не понял этого и как вы можете помочь компилятору.
  • рассмотреть: вы действительно используете лучший алгоритм? Это лучший алгоритм для вашего размера вклада?

путь google-это один из вариантов " кэшировать его.. По возможности не прикасайтесь к диску"


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

узнайте, где проводится время выяснить, что именно занимает время. Это файл IO? Это время процессора? Это сеть? Это база данных? Бесполезно оптимизировать для ввода-вывода, если это не узкое место.

Знайте Свое Окружение знание того, где оптимизировать, обычно зависит от среды разработки. В VB6, например, передача по ссылке медленнее, чем передача по значению, но в C и C++ по ссылке намного быстрее. В C разумно попробовать что-то и сделать что-то другое, если код возврата указывает на сбой, в то время как в Dot Net перехват исключений намного медленнее, чем проверка допустимого условия перед попыткой.

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

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

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

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

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

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


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


настройка ОС и фреймворка.

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

Facebook, например, изменил некоторые уровень ядра thingys в Linux изменилось, как работает memcached (например, они написали прокси-сервер memcached и используется udp вместо tcp).

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

конечно, вы всегда должны бросать в more hardware в качестве первого шага...


пройти по ссылке вместо по значению


уменьшите переменные размеры (в врезанных системах)

если размер переменной больше размера слова в определенной архитектуре, это может оказать значительное влияние как на размер кода, так и на скорость. Например, если у вас есть 16-битная система, и используйте long int переменная очень часто, а позже понять, что он никогда не может выйти за пределы диапазона (-32.768 ... 32.767) рассмотрите возможность сокращения его до short int.

из моего личного опыта, если программа готова или почти готов, но мы понимаем, что он занимает около 110% или 120% программной памяти целевого оборудования, быстрая нормализация переменных обычно решает проблему чаще, чем нет.

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

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

многие люди делают ошибку, имея переменные, которые точно хранят числовое значение единицы, которую они используют переменную для: например, их переменная time сохраняет точное количество миллисекунд, даже если актуальны только временные шаги, скажем, 50 мс. Возможно, если ваша переменная представляла 50 мс для каждого приращения одного, вы сможете вписаться в переменную, меньшую или равную размеру слова. В 8-битной системе, например, даже простое добавление двух 32-битных переменных генерирует достаточное количество кода, особенно если у вас мало регистров, в то время как 8-битные добавления являются небольшими и быстрыми.