Мы работаем над базой кода C ++ среднего размера (10Mloc), которая благодаря нашим усилиям по оптимизации становится все более медленной .
Эта кодовая база представляет собой набор библиотек, которые мы объединяем, чтобы заставить их работать. Когда была разработана общая структура взаимодействия этих библиотек, был сделан акцент на производительность, а затем, когда было добавлено больше частей, общая структура не сильно изменилась. Оптимизация проводилась по мере необходимости и по мере развития нашего оборудования. Это сделало дорогое раннее решение очевидным только намного позже. Сейчас мы находимся в точке, где дальнейшая оптимизация обходится намного дороже, так как она потребует переписывания больших частей базы кода. Мы приближаемся к нежелательному локальному минимуму, поскольку знаем, что в принципе код должен работать намного быстрее.
Существуют ли какие-либо успешные методологии, помогающие решить, какие изменения следует предпринять для эволюции кодовой базы в направлении решения с оптимальной производительностью, которое нелегко спутать с возможностями простой оптимизации?
РЕДАКТИРОВАТЬ
Чтобы ответить на вопрос, как мы в настоящее время профиль:
На самом деле у нас есть только 2 разных сценария, как этот код может быть использован, оба смущающе параллельны. Профилирование выполняется как с использованием настенного времени, усредненного по большой выборке входных данных, так и с более детальными прогонами (стоимость инструкций, неправильные прогнозы веток и проблемы с кэшированием). Это работает хорошо, поскольку мы работаем исключительно на наших чрезвычайно однородных машинах (кластер из пары тысяч идентичных машин). Поскольку мы обычно заняты всеми нашими машинами, большую часть времени работаем быстрее, это означает, что мы можем рассмотреть дополнительные новинки. Проблема, конечно, в том, что, когда появляются новые варианты ввода, они могут получить штраф за опоздание, поскольку мы убрали наиболее очевидные микроэффективности для других вариантов использования, таким образом, возможно, сузив количество «оптимально работающих» сценариев.
источник
sloc
. Я назвал это "умеренным размером", потому что я понятия не имею, что здесь считают "большим".Ответы:
Я не знаю универсального подхода к этой проблеме, но в прошлом у меня хорошо работали два взаимосвязанных подхода: из-за отсутствия лучших терминов я назвал их группировкой и горизонтальной оптимизацией .
Пакетный подход - это попытка заменить большое количество коротких, быстрых операций одной, медленной, узкоспециализированной операцией, которая в конечном итоге дает тот же результат.
примерПосле профилирования одной особенно медленной операции нашего визуального редактора правил мы не обнаружили «низко висящих плодов»: не было ни одной операции, которая занимала бы более 2% времени выполнения, но операция в целом была вялой. Однако мы обнаружили, что редактор отправляет большое количество небольших запросов на сервер. Несмотря на то, что редактор быстро обрабатывал отдельные ответы, количество взаимодействий запрос / ответ имело мультипликативный эффект, поэтому общее время, затраченное на операцию, составило несколько секунд. После тщательной каталогизации взаимодействий редактора во время этой длительной операции мы добавили новую команду в интерфейс сервера. Дополнительная команда была более специализированной, так как она принимала данные, необходимые для выполнения подмножества коротких операций, исследовал зависимости данных, чтобы выяснить окончательный набор возвращаемых данных, и предоставил ответ, содержащий информацию, необходимую для выполнения всех отдельных небольших операций за одну поездку на сервер. Это не уменьшило время обработки в нашем коде, но сократило очень большую задержку из-за удаления нескольких дорогостоящих циклических обращений клиент-сервер.
Горизонтальная оптимизация является связанной техникой, когда вы устраняете «медлительность», которая тонко распределяется между несколькими компонентами вашей системы с использованием определенной функции среды выполнения.
примерПосле профилирования длительной операции мы обнаружили, что мы делаем много вызовов через границу домена приложения (это очень характерно для .NET). Мы не могли удалить ни один из вызовов, и мы не могли связать их вместе: они приходили в разное время из самых разных разделов нашей системы, и то, что они запрашивали, зависело от результатов, полученных из предыдущих запросов. Каждый вызов требовал сериализации и десериализации относительно небольшого количества данных. Опять же, отдельные звонки были короткими по продолжительности, но очень большими по количеству. В итоге мы разработали схему, которая почти полностью избегала сериализации, заменив ее наведением указателя через границу домена приложения. Это была большая победа, потому что многие запросы от совершенно не связанных классов мгновенно стали намного быстрее в результате применения одногогоризонтальное решение.
источник
Когда вы начинаете это переписывание, вы должны сделать несколько вещей по-разному.
Первый. И самое главное. Хватит "оптимизировать". «Оптимизация» не имеет большого значения вообще. Как вы видели, только оптовые переписывают вопросы.
Следовательно.
Во-вторых. Понимать последствия каждой структуры данных и выбора алгоритма.
В третьих. Сделать фактический выбор структуры данных и алгоритма вопросом «позднего связывания». Разработка интерфейсов, которые могут иметь любую из нескольких реализаций, используемых за интерфейсом.
То, что вы делаете сейчас (переписываете), должно быть намного, намного менее болезненным, если у вас есть определенный набор интерфейсов, который позволяет вам полностью изменить структуру данных или алгоритм.
источник
++
и+=1
будет неактуально и почти неизмеримо. Это то, что ты надолго .Хорошим практическим приемом является использование вашего модульного набора тестов в качестве набора тестов производительности .
Следующий подход хорошо работал в моих базах кода:
Если вы продолжаете делать все это, то со временем средняя производительность вашей кодовой базы должна органически улучшиться.
Также было бы возможно отслеживать исторические времена испытаний и составлять графики производительности и спрогнозировать регрессии с течением времени в средней производительности. Я никогда не беспокоился об этом, главным образом, потому что немного сложно убедиться, что вы сравниваете подобное с изменением и добавляете новые тесты, но это может быть интересным упражнением, если производительность достаточно важна для вас.
источник
Ответ @dasblinkenlight указывает на очень распространенную проблему, особенно с большими базами кода (по моему опыту). Могут быть серьезные проблемы с производительностью, но они не локализованы . Если вы запускаете профилировщик, то нет никаких процедур, отнимающих достаточно времени, чтобы привлечь внимание. (Предполагая, что вы смотрите на процент времени, включающий вызываемых абонентов. Даже не думайте о «самостоятельном времени».)
На самом деле, в этом случае настоящая проблема была найдена не по профилированию, а по счастливой случайности.
У меня есть тематическое исследование, для которого есть краткое слайд-шоу в формате PDF , подробно иллюстрирующее эту проблему и способы ее решения. Суть в том, что, поскольку код работает намного медленнее, чем может быть, это означает (по определению), что избыточное время тратится на выполнение чего-то, что может быть удалено.
Если бы вы посмотрели на некоторые случайные выборки состояния программы, вы бы просто увидели, что она выполняет съемную деятельность, потому что это занимает процент времени. Вполне возможно, что съемная деятельность не ограничивается одной функцией или даже многими функциями. Это не локализуется таким образом.
Это не «горячая точка».
Это описание, которое вы делаете из того, что вы видите, которое оказывается правдой большой процент времени. Это облегчает обнаружение, но легко ли это исправить, зависит от того, сколько потребуется переписывания.
(Этот подход часто критикуют за то, что количество выборок слишком мало для статистической достоверности. Ответ дан на слайде 13 PDF. Вкратце - да, существует большая неопределенность в «измерении» потенциальной экономии, но 1) ожидаемое значение этой потенциальной экономии по существу не изменяется, и 2) когда потенциальная экономия $ x $ переводится в коэффициент ускорения на 1 / (1-x) $, она сильно отклоняется от высоких (полезных) факторов.)
источник