Стратегии оптимизации производительности в последней инстанции
На этом сайте уже много вопросов о производительности, но мне кажется, что почти все они очень проблемны и довольно узки. И почти все повторяют советы, чтобы избежать преждевременной оптимизации.
Предположим:
- код уже работает правильно
- выбранные алгоритмы уже являются оптимальными для условий проблемы.
- код был измерен, а процедуры нарушения были изолированы.
- все попытки оптимизировать также будут измеряться, чтобы они не ухудшали ситуацию
То, что я ищу здесь, - это стратегии и трюки, чтобы выжать до последних нескольких процентов в критическом алгоритме, когда больше нечего делать, но что бы это ни стоило.
В идеале, попробуйте сделать ответы языковым агностиком и укажите любые возможные варианты возможных стратегий, если это применимо.
Я добавлю ответ с моими собственными первоначальными предложениями и с нетерпением жду того, что еще может подумать сообщество Stack Overflow.
Ответы
Ответ 1
Хорошо, вы определяете проблему там, где, казалось бы, не так много возможностей для улучшения. По моему опыту, это довольно редко. Я попытался объяснить это в статье доктора Доббса в ноябре 1993 года, начав с традиционно хорошо спроектированной нетривиальной программы без очевидных потерь и проводя серию оптимизаций, пока время настенных часов не сократилось с 48 секунд. до 1,1 секунды, а размер исходного кода был уменьшен в 4 раза. Моим диагностическим средством было это. Последовательность изменений была такой:
Первой найденной проблемой было использование кластеров списков (теперь называемых "итераторами" и "классами контейнеров"), занимающих более половины времени. Они были заменены довольно простым кодом, сократив время до 20 секунд.
В настоящее время крупнейший хронометрист больше строит списки. В процентном отношении он был не таким большим, но теперь это потому, что большая проблема была устранена. Я нахожу способ ускорить его, и время падает до 17 секунд.
Сейчас труднее найти явных виновников, но есть несколько мелких, с которыми я могу что-то сделать, и время сокращается до 13 секунд.
Теперь я, кажется, ударил стену. Образцы рассказывают мне точно, что он делает, но я не могу найти ничего, что можно улучшить. Затем я размышляю над базовым дизайном программы, ее структурой, управляемой транзакциями, и спрашиваю, действительно ли весь поиск по списку, который она выполняет, требует выполнения задачи.
Затем я натолкнулся на редизайн, в котором программный код фактически генерируется (с помощью макросов препроцессора) из меньшего набора источников, и в котором программа не постоянно выясняет вещи, которые, как знает программист, достаточно предсказуемы. Другими словами, не "интерпретируйте" последовательность действий, "скомпилируйте" ее.
- Этот редизайн завершен, исходный код сокращен в 4 раза, а время сокращено до 10 секунд.
Теперь, потому что он становится таким быстрым, его трудно отобрать, поэтому я даю ему в 10 раз больше работы, но следующие времена основаны на исходной рабочей нагрузке.
Больше диагноза показывает, что он проводит время в управлении очередями. Это позволяет сократить время до 7 секунд.
Теперь большое время занимает диагностическая печать, которую я делал. Сбрось это - 4 секунды.
В настоящее время крупнейшими потребителями времени являются звонки на malloc и бесплатные звонки. Перерабатывать объекты - 2,6 секунды.
Продолжая пробовать, я все еще нахожу операции, которые не являются строго необходимыми - 1,1 секунды.
Общий коэффициент ускорения: 43,6
Сейчас нет двух одинаковых программ, но в не игрушечных программах я всегда видел такой прогресс. Сначала вы получаете легкие вещи, а затем более трудные, пока не дойдете до точки убывающей отдачи. Тогда понимание, которое вы получите, может привести к редизайну, начав новый раунд ускорений, пока вы снова не достигнете убывающей отдачи. Теперь можно подумать, не быстрее ли ++i
, i++
, for(;;)
или while(1)
: такие вопросы я часто вижу на Кару.
Постскриптум Может возникнуть вопрос, почему я не использовал профилировщик? Ответ заключается в том, что почти каждая из этих "проблем" была сайтом вызова функций, которые точно определяют образцы стека. Профилировщики даже сегодня с трудом приходят к мысли, что операторы и инструкции вызова более важны для поиска и легче исправить, чем целые функции.
Я на самом деле создал профилировщик, чтобы сделать это, но для реальной грубой близости с тем, что делает код, ничто не заменит попадание в него пальцев. Дело не в том, что количество выборок невелико, поскольку ни одна из обнаруженных проблем не настолько мала, чтобы их можно было легко пропустить.
ДОБАВЛЕНО: 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.asc и slug.zip.
ОБНОВЛЕНИЕ 2011/11/26:
В настоящее время существует проект SourceForge, содержащий исходный код в Visual C++ и подробное описание его настройки. Он проходит только первую половину описанного выше сценария и не следует точно такой же последовательности, но все равно получает ускорение на 2-3 порядка.
Ответ 2
Предложения:
- Предварительно вычислить, а не переучитывать: любые циклы или повторяющиеся вызовы, содержащие вычисления с относительно ограниченным диапазоном входных данных, рассмотрите возможность поиска (массив или словарь), который содержит результат это вычисление для всех значений в допустимом диапазоне входов. Затем используйте простой поиск внутри алгоритма.
Нижние стороны: если на самом деле используются некоторые из предварительно вычисленных значений, это может ухудшить ситуацию, и поиск может занять значительную память.
- Не используйте методы библиотеки. Большинство библиотек необходимо писать для правильной работы в широком диапазоне сценариев и выполнять нулевые проверки параметров и т.д. Повторно реализуя метод, вы можете быть в состоянии разделить много логики, которая не применяется в точном обстоятельстве, вы используете его.
Внизу: запись дополнительного кода означает большую площадь поверхности для ошибок.
- Использовать библиотечные методы: чтобы противоречить самому себе, языковые библиотеки написаны людьми, которые намного умнее вас или меня; Скорее всего, они делали это лучше и быстрее. Не выполняйте его самостоятельно, если вы не можете сделать это быстрее (т.е.: всегда измеряйте!)
- Cheat: в некоторых случаях, хотя для вашей проблемы может существовать точный расчет, вам может не понадобиться "точное", иногда приближение может быть "достаточно хорошим" и намного быстрее в сделке. Спросите себя, действительно ли имеет значение, если ответ на 1%? 5%? даже 10%?
Вниз: Ну... ответ будет не точным.
Ответ 3
Если вы больше не можете улучшить производительность, посмотрите, можете ли вы улучшить производительность воспринимаемой.
Возможно, вы не сможете быстрее выполнить ваш алгоритм fooCalc, но часто есть способы сделать ваше приложение более восприимчивым к пользователю.
Несколько примеров:
- ожидая, что пользователь собирается
запросить и начать работу над этим
до этого
- отображение результатов как
они входят, а не сразу
в конце
- Точный индикатор выполнения
Это не сделает вашу программу более быстрой, но это может сделать ваши пользователи более счастливыми со скоростью, которую вы имеете.
Ответ 4
Я провожу большую часть своей жизни именно в этом месте. Широкие штрихи - запустить профайлер и заставить его записывать:
- Кэш пропускает. Кэш данных является # 1 источником киосков в большинстве программ. Повысьте коэффициент попадания в кэш, реорганизовывая поврежденные структуры данных, чтобы иметь лучшую локальность; структуры пакетов и числовые типы, чтобы исключить потраченные впустую байты (и, следовательно, извлеченные кэширования); предваряйте данные, когда это возможно, чтобы уменьшить количество ларьков.
- Load хит-магазины. Предположения компилятора о сглаживании указателей и случаи, когда данные перемещаются между отключенными наборами регистров через память, могут вызвать определенное патологическое поведение, которое заставляет весь процессорный конвейер очищаться при загрузке op. Найдите места, где плавающие, векторы и ints отбрасываются друг к другу и устраняют их. Используйте
__restrict
либерально, чтобы пообещать компилятору об псевдониме.
- Микрокодированные операции. Большинство процессоров имеют некоторые операции, которые нельзя конвейерно обрабатывать, но вместо этого запускают крошечную подпрограмму, хранящуюся в ПЗУ. Примерами PowerPC являются целочисленные умножения, деления и сдвига по переменной. Проблема в том, что весь трубопровод останавливается, пока выполняется эта операция. Попытайтесь устранить использование этих операций или, по крайней мере, разбить их на свои составные конвейерные операции, чтобы вы могли получить преимущество суперскалярной отправки независимо от того, что делает ваша программа.
- Фиксированные ошибочные прогнозы. Они слишком пустые. Найдите случаи, когда процессор тратит много времени на заполнение трубы после ветки и использует подсказку ветки, если она доступна, чтобы заставить ее правильно прогнозировать. Или еще лучше, замените ветки условными перемещениями, где это возможно, особенно после операций с плавающей запятой, потому что их труба обычно глубже и считывает флаги условий после того, как fcmp может вызвать остановку.
- Последовательные операции с плавающей запятой. Сделайте эти SIMD.
И еще одна вещь, которую я люблю делать:
- Установите компилятор для вывода списков сборки и посмотрите, что он испускает для функций хот-спота в вашем коде. Все эти умные оптимизации, которые "хороший компилятор должен сделать для вас автоматически"? Скорее всего, ваш настоящий компилятор их не делает. Я видел, как GCC генерирует действительно WTF-код.
Ответ 5
Бросьте на него больше аппаратного обеспечения!
Ответ 6
Дополнительные предложения:
-
Избегать ввода/вывода. Любые операции ввода-вывода (диск, сеть, порты и т.д.)
всегда будет намного медленнее, чем любой код, который
выполняя вычисления, поэтому избавляйтесь от любых операций ввода-вывода, которые вы выполняете
не требуется.
-
Перенос ввода-вывода вверх: загрузите все данные, которые вы собираетесь
для того, чтобы вам нужно было вычислять аванс, чтобы вы не
повторить ввод-вывод в ядре критического
алгоритм (и, возможно, в результате повторный диск ищет, когда
загрузка всех данных одним ударом может избежать поиска).
-
Задержка ввода/вывода. Не записывайте свои результаты до тех пор, пока
вычисление завершено, сохраните их в структуре данных и
затем свалить это в один конец в конце, когда тяжелая работа
делается.
-
Threaded I/O: для этих достаточно смелых, объединить 'I/O
up-front 'или' Delay I/O 'с фактическим расчетом на
перемещая загрузку в параллельную нить, так что пока
вы загружаете больше данных, которые вы можете использовать для расчета на
данные, которые у вас уже есть, или когда вы вычисляете следующий
пакет данных можно одновременно выписать результаты
из последней партии.
Ответ 7
Поскольку многие проблемы производительности связаны с проблемами базы данных, я расскажу вам о некоторых конкретных вещах, которые нужно учитывать при настройке запросов и хранимых процедур.
Избегайте курсоров в большинстве баз данных. Избегайте зацикливания. В большинстве случаев доступ к данным должен быть установленным, а не записываться по записи. Это включает в себя не повторное использование одной хранимой процедуры записи, если вы хотите вставить 1 000 000 записей одновременно.
Никогда не используйте select *, возвращайте только нужные вам поля. Это особенно верно, если есть какие-либо объединения, поскольку поля объединения будут повторяться и, следовательно, вызывают ненужную нагрузку как на сервере, так и на сети.
Избегайте использования коррелированных подзапросов. Используйте соединения (включая, если это возможно, соединения с производными таблицами) (я знаю, что это верно для Microsoft SQL Server, но проверяйте рекомендации при использовании другого сервера).
Индекс, индекс, индекс. И обновите эту статистику, если это применимо к вашей базе данных.
Сделайте запрос sargable. Это означает, что вы избегаете действий, которые делают невозможным использование индексов, таких как использование подстановочного знака в первом символе подобного предложения или в функции в соединении или в левой части инструкции where.
Используйте правильные типы данных. Быстрее делать математику даты в поле даты, чем пытаться преобразовать тип данных строки в тип данных даты, а затем выполнить расчет.
Никогда не помещайте петлю любого типа в триггер!
В большинстве баз данных есть способ проверить, как будет выполняться выполнение запроса. В Microsoft SQL Server это называется планом выполнения. Сначала проверьте их, чтобы увидеть, где лежат проблемные области.
Рассмотрим, как часто выполняется запрос, а также сколько времени требуется для выполнения при определении того, что нужно оптимизировать. Иногда вы можете получить больше возможностей от небольшой настройки до запроса, который выполняется миллионы раз в день, чем вы можете вытереть с длинного запроса, который запускается только один раз в месяц.
Используйте какой-то инструмент профилирования, чтобы узнать, что действительно отправляется в базу данных и из нее. Я помню один раз в прошлом, когда мы не могли понять, почему страница была настолько медленной, чтобы загружаться, когда хранимая процедура была быстрой и была обнаружена через профилирование, что веб-страница запрашивала запрос много раз, а не один раз.
Профилировщик также поможет вам найти, кто блокирует кого. Некоторые запросы, которые выполняются быстро при работе в одиночку, могут стать очень медленными из-за блокировок из других запросов.
Ответ 8
Единственным важнейшим ограничивающим фактором сегодня является ограниченная память bandwitdh. Multicores просто делают это хуже, поскольку пропускная способность распределяется между ядрами. Кроме того, ограниченная область чипов, посвященная реализации кэшей, также разделяется между ядрами и потоками, что еще более усугубляет эту проблему. Наконец, межчиповая сигнализация, необходимая для поддержания когерентности различных кешей, также увеличивается с увеличением количества ядер. Это также добавляет штраф.
Это те эффекты, которые вам нужны для управления. Иногда с помощью микроконтроля ваш код, но иногда через тщательное рассмотрение и рефакторинг.
Многие комментарии уже упоминают код, дружественный к кэшу. Есть по крайней мере два отличных вкуса:
- Избегайте задержек из памяти.
- Уменьшение давления в шине памяти (полоса пропускания).
Первая проблема заключается в том, чтобы сделать ваши шаблоны доступа к данным более регулярными, позволяя аппаратной предварительной загрузке работать эффективно. Избегайте распределения динамической памяти, которое распространяет ваши объекты данных в памяти. Используйте линейные контейнеры вместо связанных списков, хэшей и деревьев.
Вторая проблема связана с улучшением повторного использования данных. Измените свои алгоритмы для работы с подмножествами ваших данных, которые подходят в доступном кеше, и повторно используйте эти данные как можно больше, пока они все еще находятся в кеше.
Более плотные данные упаковки и убедитесь, что вы используете все данные в строках кэша в горячих циклах, помогут избежать этих других эффектов и позволяют устанавливать в кеш более полезные данные.
Ответ 9
- На каком оборудовании вы работаете? Можете ли вы использовать оптимизацию для платформы (например, векторизация)?
- Можете ли вы получить лучший компилятор? Например. переключиться с GCC на Intel?
- Можете ли вы запустить параллельный алгоритм?
- Можете ли вы уменьшить промахи в кэше путем реорганизации данных?
- Вы можете отключить утверждения?
- Микро-оптимизация для вашего компилятора и платформы. В стиле "в if/else сначала ставьте наиболее распространенное утверждение"
Ответ 10
Вероятно, вам следует рассмотреть "перспективу Google", то есть определить, как ваше приложение может стать в значительной степени параллельным и параллельным, что неизбежно также означает, что в какой-то момент вам нужно будет изучить распространение вашего приложения на разных компьютерах и сетях, чтобы он мог идеально масштабируются почти линейно с аппаратным обеспечением, которое вы бросаете на него.
С другой стороны, люди Google также известны тем, что бросают много рабочей силы и ресурсов для решения некоторых проблем в проектах, инструментах и инфраструктуре, которые они используют, например оптимизация всей программы для gcc, имея специальную команду инженеров, взламывающих внутренние компоненты gcc, чтобы подготовить ее к типичным сценариям использования в Google.
Аналогично, профилирование приложения больше не означает просто профилировать программный код, а также все его окружающие системы и инфраструктуру (подумайте о сетях, коммутаторах, серверах, RAID-массивах), чтобы определить избыточность и потенциал оптимизации с системной точки вид.
Ответ 11
Хотя мне нравится ответ Майка Данлави, на самом деле это отличный ответ с поддержкой, я думаю, что это можно было бы выразить очень просто:
Узнайте, что берет на себя наибольшее количество времени, и поймите, почему.
Это процесс идентификации временных свиней, который помогает вам понять, где вы должны уточнить свой алгоритм. Это единственный всеобъемлющий ответ агностики на языке, который я могу найти для проблемы, которая уже должна быть полностью оптимизирована. Также предполагая, что вы хотите быть независимой от архитектуры в поисках скорости.
Таким образом, хотя алгоритм может быть оптимизирован, его реализация может и не быть. Идентификация позволяет вам узнать, какая часть является: алгоритмом или реализацией. Таким образом, какой бы свиньи больше не был вашим главным кандидатом на просмотр. Но так как вы говорите, что хотите выжать последние несколько процентов, вы можете также изучить более мелкие части, те части, которые вы не изучили вначале.
Наконец, немного проб и ошибок с показателями производительности по различным способам реализации одного и того же решения или потенциально разных алгоритмов может привести к пониманию, которое поможет идентифицировать время и время.
ПВД,
asoudmove.
Ответ 12
- Встроенные процедуры (исключить вызов/возврат и нажатие параметров)
- Попробуйте исключить тесты/переключатели с помощью табличного поиска (если они быстрее)
- Развернуть петли (устройство Duff) до точки, где они просто вписываются в кеш процессора
- Локализовать доступ к памяти, чтобы не вставлять кеш
- Локализовать связанные вычисления, если оптимизатор еще этого не делает,
- Устранить инварианты цикла, если оптимизатор еще не делает этого
Ответ 13
- Когда вы дойдете до того, что используете эффективные алгоритмы, это вопрос о том, что вам нужно больше скорости или памяти. Используйте кеширование для "оплаты" в памяти для большей скорости или использования вычислений для уменьшения объема памяти.
- Если возможно (и более экономически выгодно) бросить оборудование при проблеме - быстрее процессор, больше памяти или HD может решить проблему быстрее, чем пытаться ее кодировать.
- Использовать распараллеливание, если это возможно - выполнить часть кода для нескольких потоков.
- Использовать правильный инструмент для задания. некоторые языки программирования создают более эффективный код, используя управляемый код (т.е. Java/.NET), ускоряют разработку, но родные языки программирования создают более быстрый исполняемый код.
- Микро оптимизация. Только применимые вы можете использовать оптимизированную сборку для быстрой обработки небольших фрагментов кода, использование оптимизаций SSE/векторов в правильных местах может значительно повысить производительность.
Ответ 14
Разделить и покорить
Если обрабатываемый набор данных слишком велик, переместите его над кусками. Если вы правильно выполнили свой код, реализация должна быть простой. Если у вас есть монолитная программа, теперь вы знаете лучше.
Ответ 15
Прежде всего, как упоминалось в нескольких предыдущих ответах, узнайте, что укусит вашу производительность - это память или процессор, сеть или база данных или что-то еще. В зависимости от этого...
-
... если это память - найдите одну из книг, написанных много лет назад Кнутом, одним из сериалов "Искусство программирования". Скорее всего, речь идет о сортировке и поиске - если моя память ошибочна, вам придется выяснить, в чем он говорит о том, как справляться с медленным хранением данных на ленте. Мысленно преобразуем его пару памяти/ленты в пару кэш/основную память (или в пару кеша L1/L2) соответственно. Изучите все трюки, которые он описывает, - если вы найдете что-то, что решает вашу проблему, тогда нанять профессионального компьютерного ученого для проведения профессиональных исследований. Если ваша проблема с памятью случайно связана с FFT (промахи в кешах при индексированных по битам индексах при создании бабочек radix-2), то не нанимайте ученого - вместо этого ручная оптимизация проходит один за другим, пока вы не выиграете или не получите к тупику. Вы упомянули о том, чтобы выжать до последних нескольких процентов? Если это немного, вы, скорее всего, выиграете.
-
... если процессор - переключиться на язык ассемблера. Спецификация процессора исследования - что требует тиков, VLIW, SIMD. Функциональные вызовы, скорее всего, являются сменными клещами. Изучите преобразования циклов - конвейер, разворот. Умножения и деления могут быть сменными/интерполированными с битовыми сдвигами (умножения на малые целые числа могут быть заменены добавками). Попробуйте трюки с более короткими данными - если вам повезет, одна инструкция с 64 битами может оказаться заменой двумя на 32 или даже 4 на 16 или 8 на 8 бит. Попробуйте также более длинные данные - например, ваши вычисления с плавающей точкой могут оказаться более медленными, чем двойные, на конкретном процессоре. Если у вас есть тригонометрический материал, сражайтесь с заранее рассчитанными таблицами; также помните, что синус малой величины может быть заменен этим значением, если потеря точности находится в допустимых пределах.
-
... если это сеть - подумайте о сжатии данных, которые вы передаете. Замените передачу XML двоичным. Протоколы обучения. Попробуйте UDP вместо TCP, если вы можете как-то справиться с потерей данных.
-
... если это база данных, ну, перейдите на любой форум базы данных и попросите совета. Интегрированная память данных, оптимизация плана запросов и т.д. И т.д.
HTH:)
Ответ 16
Кэширование!. Дешевый способ (в усилиях программиста) сделать почти все быстрее - добавить уровень абстракции кэширования в любую область перемещения данных вашей программы. Будь то I/O или просто передача/создание объектов или структур. Часто легко добавлять кеши к классам factory и читателям/писателям.
Иногда кеш не принесет вам многого, но это простой способ просто добавить кеширование, а затем отключить его там, где это не поможет. Я часто обнаружил, что это дает огромную производительность без необходимости микроанализа кода.
Ответ 17
Я думаю, что это уже говорилось по-другому. Но когда вы имеете дело с интенсивным алгоритмом процессора, вы должны упростить все внутри самой внутренней петли за счет всего остального.
Это может показаться очевидным для некоторых, но это то, что я пытаюсь сосредоточить, независимо от языка, с которым я работаю. Например, если вы имеете дело с вложенными циклами, и вы обнаружите возможность немного понизить уровень кода, вы можете в некоторых случаях значительно ускорить ваш код. В качестве другого примера можно подумать о том, как работать с целыми числами, а не с переменными с плавающей запятой, и использовать умножение, а не деление, когда это возможно. Опять же, это вещи, которые следует учитывать для вашего самого внутреннего цикла.
Иногда вам может пригодиться выполнение математических операций над целым внутри внутреннего цикла, а затем его масштабирование до переменной с плавающей запятой, с которой вы можете работать впоследствии. Это пример жертвовать скоростью в одном разделе, чтобы улучшить скорость в другом, но в некоторых случаях окупаемость может стоить того.
Ответ 18
Я потратил некоторое время на оптимизацию бизнес-систем клиент-сервер, работающих в сетях с низкой пропускной способностью и долгой задержкой (например, спутник, удаленный, оффшорный), и смог добиться значительных улучшений производительности с довольно повторяемым процессом.
-
Измерение. Начните с понимания сетевой емкости и топологии. Поговорите с соответствующими сетевыми людьми в бизнесе и используйте основные инструменты, такие как ping и traceroute, чтобы установить (как минимум) латентность сети из каждого местоположения клиента в типичные периоды работы. Затем выполните точные измерения времени для конкретных функций конечного пользователя, которые отображают проблематичные симптомы. Запишите все эти измерения вместе со своими местоположениями, датами и временем. Подумайте о том, как использовать функциональные возможности конечного пользователя для тестирования производительности сети в своем клиентском приложении, чтобы ваши опытные пользователи могли участвовать в процессе улучшения; расширение прав и возможностей таких людей может иметь огромное психологическое воздействие, когда вы имеете дело с пользователями, разочарованными плохо функционирующей системой.
-
Анализ. Использование любых методов ведения журнала, позволяющих точно определить, какие данные передаются и получаются во время выполнения затронутых операций. В идеале ваше приложение может захватывать данные, передаваемые и полученные как клиентом, так и сервером. Если они включают также временные метки, еще лучше. Если достаточное количество журналов недоступно (например, закрытая система или невозможность развернуть модификации в производственной среде), используйте сетевой сниффер и убедитесь, что вы действительно понимаете, что происходит на сетевом уровне.
-
Кэш. Ищите случаи, когда статические или редко изменяемые данные передаются повторно и рассматривают соответствующую стратегию кэширования. Типичные примеры включают значения "выбрать список" или другие "ссылочные объекты", которые могут быть удивительно большими в некоторых бизнес-приложениях. Во многих случаях пользователи могут принять, что они должны перезапустить или обновить приложение, чтобы обновлять редко обновляемые данные, особенно если он может существенно сократить время с дисплея обычно используемых элементов пользовательского интерфейса. Убедитесь, что вы понимаете реальное поведение уже внедренных элементов кеширования - многие распространенные методы кэширования (например, HTTP ETag) по-прежнему требуют сетевого взаимодействия, чтобы обеспечить согласованность, и когда время ожидания сети дорогое, вы можете вообще избежать этого другой подход к кэшированию.
-
Parallelise. Ищите последовательные транзакции, которые логически не нужно выписывать строго последовательно, и повторно обрабатывайте систему для их параллельного вывода. Я имел дело с одним случаем, когда сквозной запрос имел встроенную задержку сети ~ 2 с, что не было проблемой для одной транзакции, но когда требовалось 6 последовательных 2-раундовых поездок, прежде чем пользователь восстановил контроль над клиентским приложением, это стало огромным источником разочарования. Обнаружение того, что эти транзакции были фактически независимыми, позволяло выполнять их параллельно, уменьшая задержку конечного пользователя до очень близкого к стоимости одной поездки в оба конца.
-
Комбинация. Если последовательные запросы должны выполняться последовательно, ищите возможности объединить их в один более полный запрос. Типичные примеры включают создание новых объектов, за которыми следуют запросы для связывания этих объектов с другими существующими объектами.
-
Сжатие. Ищите возможности использовать сжатие полезной нагрузки, заменяя текстовую форму бинарным или используя настоящую технологию сжатия. Многие современные (т.е. В течение десятилетия) технологические стеки поддерживают это почти прозрачно, поэтому убедитесь, что он настроен. Меня часто удивляло значительное влияние сжатия, когда казалось очевидным, что проблема связана с фундаментальной задержкой, а не пропускной способностью, открывающейся после того, как она позволила транзакции соответствовать одному пакету или иным образом избежать потери пакетов и, следовательно, влияние на производительность.
-
Повторить. Вернитесь к началу и повторите измерение своих операций (в тех же местах и времени) с улучшениями, запишите и сообщите о своих результатах. Как и при любой оптимизации, некоторые проблемы, возможно, были решены, если выставляете других, которые теперь доминируют.
В приведенных выше шагах я сосредотачиваюсь на процессе оптимизации, связанного с приложением, но, конечно же, вы должны убедиться, что основная сеть сама настроена наиболее эффективным образом для поддержки вашего приложения. Задействуйте сетевых специалистов в бизнесе и определите, могут ли они применять улучшения емкости, QoS, сетевое сжатие или другие методы решения этой проблемы. Обычно они не понимают ваши потребности в приложениях, поэтому важно, чтобы вы были готовы (после шага анализа), чтобы обсудить это с ними, а также сделать бизнес-кейс за любые расходы, которые вы собираетесь попросить, чтобы они понесли, Я встречал случаи, когда ошибочная конфигурация сети приводила к тому, что данные приложений передавались по медленной спутниковой линии, а не по сухопутной линии, просто потому, что она использовала TCP-порт, который не был "хорошо известен" специалистам по сетям; очевидно, устранение подобной проблемы может оказать существенное влияние на производительность, при этом не требуется никакого программного кода или изменений конфигурации.
Ответ 19
Очень сложно дать общий ответ на этот вопрос. Это действительно зависит от вашей проблемной области и технической реализации. Общий метод, который является довольно нейтральным языком: Определите горячие точки кода, которые не могут быть устранены, и оптимизируйте код ассемблера вручную.
Ответ 20
Последние несколько% - это очень важная вещь для процессора и приложений....
-
Архитектуры кэша
- отличаются, некоторые чипы имеют встроенную ОЗУ
вы можете напрямую сопоставить, ARM (иногда) имеет вектор
unit, SH4 - полезный код операции матрицы. Есть ли GPU -
возможно, шейдер - это путь. TMS320 очень
чувствительных к ветвям внутри петель (поэтому отдельные петли и
если возможно, переместите условия снаружи.)
Список продолжается... Но такие вещи действительно
последнее средство...
Создайте для x86 и запустите Valgrind/Cachegrind против кода
для правильного профилирования производительности. Или Texas Instruments '
CCStudio имеет сладкий профилировщик. Тогда вы действительно будете знать, где
для фокусировки...
Ответ 21
Did you know that a CAT6 cable is capable of 10x better shielding off extrenal inteferences than a default Cat5e UTP cable?
Для любых проектов, отличных от автономных, при наличии лучшего программного обеспечения и лучшего оборудования, если ваша пропускная способность слаба, то эта тонкая линия собирается сжать данные и дать вам задержки, хотя и в миллисекундах... но если вы говорите последние капли, что некоторые капли получены, 24/7 для любой отправленной или полученной упаковки.
Ответ 22
Не так глубоко или сложно, как предыдущие ответы, но здесь говорится:
(это больше начального/промежуточного уровня)
- Очевидное: сухое
- запустите циклы назад, чтобы вы всегда сравнивали с 0, а не с переменной
- использовать побитовые операторы всякий раз, когда вы можете
- перерыв повторяющегося кода в модули/функции
- объекты кэша
- Локальные переменные имеют небольшое преимущество в производительности.
- максимально ограничить обработку строк
Ответ 23
Невозможно сказать. Это зависит от того, как выглядит код. Если мы можем предположить, что код уже существует, мы можем просто взглянуть на него и выяснить, как его оптимизировать.
Улучшенная локальность кеша, разворачивание цикла, Попробуйте устранить длинные сети зависимостей, чтобы получить лучший уровень уровня parallelism. Предпочитайте условные перемещения по веткам, когда это возможно. При необходимости используйте SIMD-инструкции.
Понимайте, что делает ваш код, и понимайте, на каком оборудовании он работает. Тогда становится довольно просто определить, что вам нужно сделать, чтобы повысить производительность вашего кода. Это действительно единственный поистине общий совет, о котором я могу думать.
Ну, это, и "Покажите код на SO и попросите совета по оптимизации для этой конкретной части кода".
Ответ 24
Если лучшее оборудование - это опция, то обязательно для этого. В противном случае
- Проверьте, что вы используете лучшие параметры компилятора и компоновщика.
- Если функция hotspot в другой библиотеке часто используется для вызова вызывающего абонента, подумайте о перемещении или клонировании ее в модуль вызывающих абонентов. Устраняет некоторые из служебных задач вызова и может улучшить хиты кэша (cf как AIX привязывает strcpy() статически к отдельно связанным общим объектам). Это, конечно же, может уменьшить кеш-хиты, поэтому одна мера.
- Посмотрите, есть ли возможность использовать специализированную версию процедуры hotspot. Нижняя сторона поддерживает более одной версии.
- Посмотрите на ассемблер. Если вы считаете, что это может быть лучше, подумайте, почему компилятор не понял этого и как вы могли помочь компилятору.
- Подумайте: вы действительно используете лучший алгоритм? Это лучший алгоритм для вашего размера ввода?
Ответ 25
Путь Google - это один из вариантов "Cache it.. По возможности не касайтесь диска"
Ответ 26
Вот некоторые быстрые и грязные методы оптимизации, которые я использую. Я считаю, что это оптимизация "первого прохода".
Узнайте, где потрачено время Узнайте, что именно занимает время. Это файл IO? Это время процессора? Это сеть? Это база данных? Это бесполезно оптимизировать для IO, если это не узкое место.
Знай свою среду Знать, где оптимизировать, как правило, зависит от среды разработки. Например, в VB6 передача по ссылке медленнее, чем передача по значению, но в C и С++ ссылочная ссылка значительно быстрее. В C, разумно попробовать что-то и сделать что-то другое, если код возврата указывает на сбой, а в Dot Net исключения catching намного медленнее, чем проверка действительного условия перед попыткой.
Индексы. Создавайте индексы по часто запрашиваемым полям базы данных. Вы можете почти всегда торговать пространством для скорости.
Избегайте поиска. Внутри цикла, который нужно оптимизировать, я не должен делать никаких поисков. Найдите смещение и/или индекс за пределами цикла и повторно используйте данные внутри.
Свернуть IO попытайтесь сконструировать таким образом, чтобы уменьшить количество раз, когда вы должны читать или писать, особенно по сетевому соединению
Уменьшить абстракции Чем больше слоев абстракции должен работать код, тем медленнее это. Внутри критического цикла уменьшите абстракции (например, выявите методы нижнего уровня, которые избегают дополнительного кода)
Spawn Threads для проектов с пользовательским интерфейсом, создавая новый поток для создания более медленных задач, делает приложение более отзывчивым, хотя это не так.
Предварительная обработка. Вы можете обычно торговать пространством для скорости. Если есть расчеты или другие интенсивные операции, посмотрите, можете ли вы предварительно скопировать некоторую информацию до того, как находитесь в критическом цикле.
Ответ 27
Добавление этого ответа, поскольку я не видел его включенным во всех остальных.
Минимизировать неявное преобразование между типами и знаками:
Это, по крайней мере, относится к C/С++. Даже если вы уже считаете, что у вас нет конверсий, иногда полезно тестировать добавление предупреждений компилятора о функциях, требующих производительности, особенно в случае отсутствия конверсий внутри циклов.
GCC spesific: вы можете проверить это, добавив несколько верных прагм вокруг вашего кода,
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic error "-Wsign-conversion"
# pragma GCC diagnostic error "-Wdouble-promotion"
# pragma GCC diagnostic error "-Wsign-compare"
# pragma GCC diagnostic error "-Wconversion"
#endif
/* your code */
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
Я видел случаи, когда вы можете получить ускорение на несколько процентов, уменьшая количество конверсий, вызванных предупреждениями вроде этого.
В некоторых случаях у меня есть заголовок со строгими предупреждениями, которые я включил для предотвращения случайных преобразований, однако это компромисс, поскольку вы можете в конечном итоге добавить много отливок к тихим намеренным преобразованиям, которые могут просто сделать код более захламленный для минимальной прибыли.
Ответ 28
Иногда может измениться макет ваших данных. В C вы можете переключиться с массива или структур на структуру массивов или наоборот.
Ответ 29
Подгоняйте ОС и инфраструктуру.
Это может показаться излишним, но подумайте об этом так: Операционные системы и Frameworks предназначены для выполнения многих задач. Ваше приложение выполняет только конкретные вещи. Если бы вы могли заставить ОС выполнить именно то, что нужно вашему приложению, и чтобы ваше приложение понимало, как работает фреймворк (php,.net, java), вы можете значительно улучшить свое оборудование.
Facebook, например, изменил некоторые kernel level thingys в Linux, изменил работу memcached (например, они написали прокси-сервер memcached и используется udp вместо tcp).
Другим примером этого является Window2008. У Win2K8 есть версия, вы можете установить только базовую ОС, необходимую для запуска приложений X (например, Web-Apps, Server Apps). Это уменьшает значительную часть накладных расходов, которые ОС выполняет при запуске процессов, и дает вам лучшую производительность.
Конечно, вы всегда должны использовать больше аппаратного обеспечения в качестве первого шага...
Ответ 30
передать по ссылке вместо значения