Идеи для хороших оптимизаций производительности в C++

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

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

  • Имеет потенциал для базового сокращения использования пространства, например,> 50%-е сокращение данных тяжелая область.

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

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

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

  • При сортировке проверьте, что данные уже не находятся в порядке сортировки (конкретно, где qsort использовался),

  • Сохраните GUI и обрабатывающий в отдельных потоках (Сбои хорошие критерии того, чтобы быть легким реализовать, но IMO, все еще стоящий)

  • Где у Вас есть переменные локального класса, которые имеют значительную конструкцию / время разрушения, в в большой степени используемых функциях членства, делают их частными участниками класса. Особенно верный для динамических массивов и строк, особенно MFC CArrays и CStrings.

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

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

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

    CString MyFunc (удваивают x, дважды y),

менее эффективно, чем

void  MyFunc(double x, double y, CString &Result)

на самом деле избегайте CStrings и большей части MFC в любой производительности критическая область кода. (Редактирование: это может в более общем плане отрицаться RVO, хотя не для CStrings в моем приложении),

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

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

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

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

Стратегии оптимизации производительности последней инстанции?

Что я могу использовать для профилирования кода C++ в Linux?

5
задан Community 23 May 2017 в 10:27
поделиться

9 ответов

Мое решение состоит в преобразовании результата сущностей в список и после , которые применяют Contains ().

Пример:

var items = db.InventoryItem
                .Include("Kind")
                .Include("PropertyValues")
                .Include("PropertyValues.KindProperty")
                .ToList()
                .Where(itm => valueIds.Contains(itm.ID));
-121--2722947-

Monadic return :

return x

Или:

(:[]) x

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

-121--4746348-

CString MyFunc (double x, double y)

менее эффективен, чем

void MyFunc (double x, double y, CString & Result)

Если MyFunc написан чисто, они должны быть примерно одинаковыми. Компилятор должен иметь возможность использовать NRVO . Похоже, вы профилировали и обнаружили, что его нет - я просто говорю, что он может больше соответствовать вашим критериям, например, «минимальная обфускация кода», чтобы немного переставить саму функцию, чтобы позволить NRVO произойти.

Еще несколько вещей, которые нужно попробовать:

  1. memoization (похоже на ваше кэширование повторных поисков, но более сосредоточено на разрешении дерева/графика, при наличии).
  2. Использование поплавков вместо двойных, если вам не нужны дополнительные точность (или, может быть, даже ints, если вы может).
  3. Используйте типы для пометки допущений (вы упомянули сортированные массивы - другой общий - строчный последовательности). Создание производного или тип оболочки (например, Sorted < T > ), которая сделать такие предположения явными. Это способ, если у вас есть метод, который принимает a Отсортированный < вектор < T > > , например, и вы даете ему отсортированный вектор, он проходит прямо - но если дать ему вектор < T > , он должен будет создал отсортированный < вектор < T > > , в какой момент он будет сортировать его. Можно вручную подтвердить, что он отсортирован используя альтернативный конструктор, но это значительно облегчает ношение ваши предположения вокруг и, возможно, ловите места, которые у вас могут быть в противном случае пропущено.
  4. Не сдавайтесь на линии и т.д. Убедитесь, что вы полностью осведомлены о том, когда они должны помочь и когда должно препятствовать. В некоторых случаях они действительно может изменить ситуацию - но возможно, нет, если вы просто имеете дело с они произвольно.
  5. Вы можете воспользоваться преимуществами flyweight или объединенного объекта распределители .
  6. При многопоточности постарайтесь минимизировать любые взаимодействия, чтобы можно уменьшить количество кода для этого требуется блокировка. Часто принимая копии даже довольно дорогих объекты могут быть меньше накладных расходов чем мьютекс. Очевидное использование преимуществ атомных типов, где можно.
5
ответ дан 18 December 2019 в 05:49
поделиться

Я опасаюсь больших и сложных структур данных. Мне нравятся большие и простые структуры данных; в частности массивы. Когда у меня есть большие и простые структуры данных, я могу попытаться делать умные вещи с доступом к памяти, чтобы действительно оптимизировать использование кеша; в частности, мозаика памяти. Не уверен, что это полезно для вас, но в целом, учитывая ваш набор требований и существующее понимание вашего кода, я бы искал способы оптимизировать получение данных из ОЗУ в ЦП.

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

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

Черт возьми, мне так нравится ваш вопрос, что я его поддерживаю.

С уважением

Марк

3
ответ дан 18 December 2019 в 05:49
поделиться

Хотя официально не является частью стандарта STL, hash_map и hash_set обычно используются для улучшения времени поиска......

http://msdn.microsoft.com/en-us/library/0d462wfh%28VS.80%29.aspx

Итак, длинная история короткая - нет.

-121--222676-

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

<antcallback target="target1" return="myVar"/>
<antcall target="display"/>

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

-121--3909752-

Я удивлен, что никто, кажется, не ответил на один момент. В вашем вопросе вы упоминаете использование qsort () . В некоторых случаях можно значительно повысить скорость, используя std:: sort () вместо qsort () . Размер улучшения обычно зависит от того, насколько сложна ваша функция сравнения, но я нашел 2:1 улучшение довольно типичным, и 5:1 или даже 10:1 иногда возможно.

3
ответ дан 18 December 2019 в 05:49
поделиться

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

Не тратьте зря время, вручную отмечая функции как inline . Не тратьте время на то, чтобы собрать какой-то универсальный «Словарь оптимизаций».

97% вашего кода просто не имеет значения с точки зрения производительности. Если вы попытаетесь применить оптимизации независимо от того, что они делают и где они полезны, вы потратите 97% своего времени.Все это время можно было потратить на оптимизацию 3% кода, который действительно имел значение. (Кстати, именно это Кнут на самом деле имел в виду, говоря о «корне всего зла». Не то, чтобы оптимизацию проводить не следует, но если у вас нет в голове определенного фрагмента кода, который, как вы уже знаете, является горячей точкой, ваши оптимизации будет 1) преждевременным и 2) неэффективным)

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

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

Третий совет по оптимизации: изучите систему, в которой вы работаете. Структурируйте свой код, чтобы использовать пространственную и временную локальность, чтобы минимизировать промахи в кэше. Простое переключение обхода 2D-массива с основного столбца на порядок строк может легко удвоить производительность. Имейте в виду, что и компилятор, и ЦП будут переупорядочивать инструкции для повышения пропускной способности, но ветви ограничивают их способность делать это, поэтому попытайтесь структурировать свой код, чтобы получить достаточно большие базовые блоки без переходов в них или из них. Если вы работаете на ЦП, поддерживающем инструкции SIMD, подумайте, можно ли их эффективно использовать.Если вам нужно действительно погрузиться в оптимизацию на уровне инструкций, убедитесь, что вы понимаете задержки используемых инструкций. Для тяжелого кода с плавающей запятой имейте в виду, что инструкции FP в целом не будут автоматически переупорядочены компилятором или процессором. Поскольку операции FP имеют довольно большие задержки, цепочки зависимостей могут быть настоящим убийцей производительности. Разделение их вручную может значительно ускорить ваш код. Точно так же избегайте зависимости от памяти. Код, который сначала пишет, а затем читает адрес, будет медленным. Если один или оба доступа к памяти не могут быть оптимизированы, вам нужно дождаться завершения записи (что в противном случае могло бы произойти в фоновом режиме без остановки процессора), прежде чем начинать чтение. Поместите все часто используемые данные во временные файлы, чтобы избежать сглаживания.

Что касается оптимизации «больших сложных наборов данных», как вы спросили? Я понятия не имею. Особенность сложных наборов данных в том, что у них очень мало общего. Не существует общего способа их оптимизации.

Последнее предложение: похоже, что вы на самом деле не пишете C ++. Вы говорите о ручной реализации динамических массивов, вы говорите о реаллоках и классах MFC. Похоже, вы пишете "C с классами". Используйте стандартную библиотеку. std :: vector уже существует. То же самое и с std :: string . Стандартная библиотека C ++ обладает тем замечательным свойством, что она чрезвычайно эффективна. Чего нельзя сказать о классах MFC.

3
ответ дан 18 December 2019 в 05:49
поделиться

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

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

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

3
ответ дан 18 December 2019 в 05:49
поделиться

пожалуйста, не бросайте старую "оптимизация - корень всего зла" , это совершенно не имеет отношения к этому вопросу

Да, и тогда у вас есть такие вещи, как:

При сортировке проверьте, что данные еще не находятся в порядке сортировки

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

и будет проигнорирован.

В самом деле? Этот тон никуда не годится. ИМО. YMMV.

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

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

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

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

Изменить: поскольку вы говорите, что не знали RVO: попробуйте прочитать семантику перемещения и особенно эту библиотеку: переместить библиотеку из Adobe. Думаю, в Boost будет что-то подобное.

Правка №2: Еще две вещи:

  • Арифметика с фиксированной запятой может быть хорошей вещью для поиска
  • Используйте таблицы поиска как можно больше
9
ответ дан 18 December 2019 в 05:49
поделиться

+1 Хороший вопрос.

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

Я не знаю, откуда это 97% происходит от. Я учил правило 80/20 - 20% кода проходит 80% времени, что интересно также применимо к другим вещам, кроме программного обеспечения. Во всяком случае ...

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

Несколько общих оптимизаций, которые я обнаружил, может быть полезен:

  1. линейные поиски часто несут ответственность за довольно много накладных расходов. Часто их можно заменить на двоичный поиск сортированной коллекции.

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

  3. Вставка в отсортированный набор - может использовать двоичный поиск, чтобы найти правильное место вместо наивных (хотя часто достаточно хороших). Реализация «придерживайтесь его на конце, затем сортировать».

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

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

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

1
ответ дан 18 December 2019 в 05:49
поделиться

Конечный автомат, кажется, часто используется для аналогичных задач, например, http://www.codeproject.com/KB/string/civstringset.aspx

-121--3514159-

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

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

-121--1287977-

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

0
ответ дан 18 December 2019 в 05:49
поделиться

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

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

Есть некоторые ключевые моменты, которые вам нужно понять.

  • Система крючков. Это способ Друпала сделать ООП. Идея заключается в том, что модули или ядро Drupal могут определять и запускать крючки, когда происходит событие, на которое другие модули хотят реагировать. Примером может служить создание пользователя или дисплей узла. Для реализации крючка просто создается функция с правильным именем. Необходимо заменить «hook» в имени hook на имя модуля.
  • Drupal имеет много функций API, и требуется время, чтобы найти их и научиться их использовать. Не торопитесь, поскольку использование правильных функций API может иметь большое значение. Drupal имеет хороший API сайт

Из вашего описания вы должны взглянуть на следующие функции:

  • hook _ user
  • hook _ perms
  • hook _ menu
  • db _ query
-121--4605321-

http://www.in-ulm.de/~mascheck/various/ash/#busybox из ссылки кажется busybox ash является debian dash.

-121--2225934-

Я сочувствую вашей позиции, особенно

Я сидел перед профилировщиком результаты за последние три дня

Я предполагаю, что вы проделали хорошую работу по интеллектуальному кодированию приложения. Теперь, вот что я предлагаю:

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

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

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

... и удачи. Дайте нам знать, как это работает.

2
ответ дан 18 December 2019 в 05:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: