it-roy-ru.com

Список распространенных методов оптимизации C++

Могу ли я иметь большой список распространенных практик оптимизации C++?

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

22
yoitsfrancis

Два способа написания лучших программ:

Лучше всего использовать язык

  1. Код завершен Стивом Макконнеллом
  2. Эффективный C++
  3. Исключительный C++

профиль вашей заявки

  1. Определите, какие области кода занимают сколько времени
  2. Посмотрите, можете ли вы использовать более совершенные структуры данных/алгоритмы, чтобы ускорить процесс

Существует не так много оптимизации для конкретного языка - это ограничено использованием языковых конструкций (учитесь на # 1). Основным преимуществом является № 2 выше.

17
Sesh

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

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

void FlipBuffer(unsigned char *start, unsigned char *end)
{
    unsigned char temp;

    while (start <= end) {
        temp = _bitRev[*start];
        *start++ = _bitRev[*end];
        *end-- = temp;
    }
 }

который поворачивает 1-битный буфер кадра на 180 градусов. _bitRev - это 256-байтовая таблица обращенных битов. Этот код настолько узок, насколько вы можете его получить. Он работал на контроллере лазерного принтера 68 МГц с частотой 8 МГц и занимал примерно 2,5 секунды для листа бумаги легкого размера. Чтобы сэкономить детали, клиент не мог выдержать 2,5 секунды. Решение было идентичным алгоритму для этого. Разница была в том, что

  1. Я использовал таблицу 128 КБ и оперировал словами вместо байтов (68 КБ намного лучше для слов)
  2. Я использовал устройство Даффа, чтобы развернуть петлю так, чтобы она поместилась в короткой ветви 
  3. Я положил в оптимизации, чтобы пропустить пустые слова
  4. Я, наконец, переписал его на ассемблере, чтобы воспользоваться инструкцией sobgtr (вычесть единицу и увеличить на большую) и получить «свободный» постинкремент и предварительные декременты в нужных местах.

Итак, 5x: без изменения алгоритма.

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

24
plinth

Не забывайте о нескольких вещах:
- «Мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация - корень всего зла». (с) Дональд Кнут
- Мы могли бы получить больше, если будем оптимизировать алгоритмы, а не код.
- Мы оптимизируем только медленные части существующего кода, который будет обнаружен профилировщиком или другим специальным инструментом.

12
bayda

Агнер Фог проделал огромную работу, проанализировав результаты работы нескольких компиляторов относительно конструкций C++ .... Его работу вы найдете здесь: http://www.agner.org/optimize/ .

Intel также предлагает отличный документ - «Справочное руководство по оптимизации архитектур Intel® 64 и IA-32», которое вы найдете по адресу http://www.intel.com/products/processor/manuals/index.htm , Хотя он в основном предназначен для архитектур IA-32, он содержит общие рекомендации, которые можно применять на большинстве платформ. Очевидно, это и руководство Агнера Фога немного пересекаются.

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

9
Raphaël Saint-Pierre

Вы можете быть заинтересованы в этом: Оптимизация C++ Wikibook

5
sivabudh

У меня нет сайта с головой, но книга Саттера "Исключительный C++" превосходна для разработки на C/C++. Я настоятельно рекомендую каждому программисту на С ++ прочитать эту книгу, так как она дает отличное представление не только об оптимизации, но и об умном использовании языка, так что вы будете программировать действительно исключительно.

4
Jordan Parmer

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

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

Классическим примером программного обеспечения является SGI Indy Irix 5.1 , что отчасти является причиной того, что пользователи, интенсивно использующие графику, теперь имеют компьютеры Mac и Windows, а не блоки SGI. 

"Что самое страшное в производительности 5.1, так это то, что никто точно не знает, куда она пошла. Если вы начнете задавать вопросы, у вас будет много указаний и теорий, но мало фактов. В майском отчете я предложил" 5% теория ", которая гласит, что каждая мелочь, которую мы добавляем (Motif, интернационализация, перетаскивание, DSO, несколько шрифтов и т. д.), стоит примерно 5% от машины. После 15 или 20 из них большая часть производительности ушел. "

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

3
Pete Kirkham

Вы просили сайты/источники, содержащие мудрость оптимизации.

Некоторые хорошие были предложены.

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

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

ДОБАВЛЕНО:

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

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

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

Вот в чем проблема с "общей мудростью". Часто это полная противоположность мудрости.

2
Mike Dunlavey

++ p обычно быстрее, чем p ++, а --p быстрее, чем p--, особенно для объектов типов с перегруженными префиксными и постфиксными операторами увеличения и уменьшения, потому что префикс формы просто увеличивает или уменьшает что-то и возвращает новое значение, тогда как Постфиксная форма что-то увеличивает или уменьшает, но должна где-то хранить старое значение, чтобы вернуть его. То есть вместо (замените int вашим любимым классом здесь)

for ( int i ( 0); i < x; i++)

всегда пиши

for ( int i ( 0); i < x; ++i)
2
dmityugov

Большинство методов являются специфичными для compiler, так как разные компиляторы оптимизируют по-разному. 

Если вам нужны советы по оптимизации, не зависящие от компилятора, вот два для вас:

  1. Не делай этого. 
  2. (только для экспертов!): Пока не делайте этого.

(извинения Майкл А. Джексон )

2
T.E.D.

Извините, у меня нет ссылок для вас, но у меня есть еще один анекдот, чтобы добавить в кучу.

У меня была довольно большая std :: map, которую я генерировал, используя в качестве ключа объект CString от Microsoft. Производительность была неприемлема. Поскольку все мои строки были одинаковы по длине, я создал оболочку класса вокруг старомодного массива символов фиксированного размера для эмуляции интерфейса CString. К сожалению, я не могу вспомнить точное ускорение, но оно было значительным, и результирующая производительность была более чем адекватной.

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

1
Mark Ransom

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

1
andrepd

Мета-программирование шаблонов можно использовать для перехода от динамического полиморфизма к компиляции временного полиморфизма, создавая безумно оптимальный код в процессе. Alexandrescu's Modern C++ Design - это хорошая книга, которая подробно описывает TMP. Не каждая страница посвящена оптимизации, но это часто повторяется при разработке программы.

1
justin

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

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

Единственный совет по оптимизации, специфичный для C++, который я могу придумать, это «понять, что означает ваш код». Понимать, когда C++ копирует временные данные, понимать, какие конструкторы и деструкторы вызываются, когда.

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

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

Правка : Комментарий спрашивал о том, что функторы против указателей на функции являются встроенными. Вот объяснение:

Функции обычно составляются изолированно. Так что же знает компилятор о функции F, которая принимает указатель функции FP в качестве аргумента? Ничего, он должен посмотреть, откуда вызывается F, и может быть там он может найти определенную подсказку относительно того, на какую функцию FP указывает. Если он может определить, что при вызове отсюда, FP будетВСЕГДАуказывать на функцию G, то да, он может сделать встроенную версию F с G внутри нее, для этого конкретного сайта вызова. Но он не может просто встроить G без встраивания F, потому что F может вызываться и из других мест, где ему передается другой указатель на функцию. И даже тогда, это требует некоторых дорогостоящих глобальных оптимизаций, чтобы определить, что все может быть встроено.

Представьте, что вместо этого вы передаете такой функтор:

struct Ftor {
  void operator()() { ... }
};

поэтому функция F выглядит так:

void F(const FTor& ft) {
  ...
  ft();
  ...
}

теперь компилятор знает точно какая функция вызывается: строка 2 в функции вызывает Ftor :: operator (). И поэтому звонок может быть легко встроен.

Конечно, на практике вы, как правило, шаблонируете его, поэтому функцию можно вызывать с любым типом функтора:

template <typename F>
void F(const F& ft) {
  ...
  ft();
  ...
}
1
jalf
  1. Профилируйте свой код, чтобы найти то, что на самом деле занимает больше всего времени
  2. Убедитесь, что вы используете правильный алгоритм
  3. Если вы делаете много перебора чисел, будьте дружелюбны с вашим кешем и постарайтесь сделать все возможное сразу для куска данных, чтобы его не приходилось многократно загружать в кеш.
1
Mr Fooz

Вот пара поймать все пути для оптимизации. 

One пути для проблем оптимизации не существует ... они всегда настраиваются вручную на аппаратные/программные/системные соображения. 


Предполагая, что у вас есть лучший алгоритм:

  1. компилировать с «показом сборки сборки» и «самой высокой оптимизацией»
  2. посмотрите на вывод сборки
  3. выявить недостатки, которые разрушают оптимизацию компилятора или плохое кеширование 
  4. Перекодировать фрагмент
  5. если это все еще плохо, вернитесь к 2.
  6. сделанный

Пример можно увидеть здесь: Какой самый быстрый способ поменять значения в C?


Общие советы:

http://www.ceet.niu.edu/faculty/kuo/exp/exp6/figuree6-1.jpg :

  • Попробуйте сначала с плавающей точкой
  • Попробуйте в секунду с фиксированной точкой
  • Если вы действительно несопоставимы и у вас много времени и денег, попробуйте это в Ассамблее

http://www.asc.edu/seminars/optimization/fig1.gif :

  • Проверьте, привязан ли он к памяти OR ввода-вывода OR
  • Атака, которая всегда является ограничивающим фактором

0
Trevor Boyd Smith

Есть много вещей, которые вы можете сделать, чтобы оптимизировать код C++. Некоторые из более широких предложений перечислены выше. 

Пара конкретных примеров: 

  1. Использование Struts of Arrays в противоположность Array of Structs (классический пример DOP vs OOP) 
  2. Использование restrict в C++, когда вы знаете, что два указателя не будут указывать на одну и ту же область памяти

В общем, следование фундаментальной парадигме программирования, такой как Data-Oriented Programming, даст более высокую производительность, поскольку DOP специально сформулирована для того, чтобы сосредоточиться на производительности (во всех формах: схемы памяти, согласованность кэша, затраты времени выполнения и т.д.)

Более подробная информация здесь: https://people.cs.clemson.edu/~dhouse/courses/405/papers/optimize.pdf

0
ruckarucka

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

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

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

0
siddhadev