Подтвердить что ты не робот

Общие советы по повышению производительности С++

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

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

Спасибо:)

4b9b3361

Ответ 1

Несколько советов в Эффективный С++, Более эффективный С++, Эффективный STL и Стандарты кодирования С++ эта строка.

Простой пример такого наконечника: используйте preincrement (++ i), а не постинкремент (i ++), когда это возможно. Это особенно важно при использовании итераторов, поскольку постинктация включает в себя копирование итератора. Оптимизатор, возможно, сможет отменить это, но вместо этого не нужно писать дополнительную запись, поэтому зачем рисковать?

Ответ 2

Если я правильно вас понимаю, вы спрашиваете об избегании Преждевременной пессимизации, что является хорошим дополнением к предотвращению преждевременной оптимизации. # 1, чтобы избежать, основываясь на моем опыте, - не копировать большие объекты, когда это возможно. Это включает в себя:

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

Эта последняя пуля требует некоторого объяснения. Я не могу сказать, сколько раз я видел это:

class Foo
{
    const BigObject & bar();
};

// ... somewhere in code ...
BigObject obj = foo.bar();  // OOPS!  This creates a copy!

Правильный путь:

const BigOject &obj = foo.bar();  // does not create a copy

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

Ответ 3

Одной из хороших отправных точек является Sutter Guru of the week" и "Исключительные книги на C++", которые выросли из этого.

Ответ 4

Несколько моих питомцев:

  • Не объявлять (фактически, определять) переменные объекта перед их использованием/инициализацией (как в C). Это требует, чтобы выполнялись функции оператора конструктора AND и для сложных объектов, что может быть дорогостоящим.
  • Предпочитайте предварительный приращение для последующего приращения. Это будет иметь значение только для итераторов и пользовательских типов с перегруженными операторами.
  • Используйте наименьшие примитивные типы. Не используйте long int для хранения значения в диапазоне 0..5. Это уменьшит общее использование памяти, улучшит локальность и, следовательно, общую производительность.
  • Использовать память кучи (динамическое распределение) только при необходимости. Многие программисты на C++ используют кучу по умолчанию. Динамические распределения и освобождение от оплаты дорогостоящие.
  • Минимизировать использование временных рядов (особенно при обработке на основе строк). Stroustrup представляет собой хорошую технику для определения логического эквивалента трехмерных и арифметических операторов более высокого порядка в "языке программирования С++".
  • Знайте свои параметры компилятора/компоновщика. Также знаете, какие из них вызывают нестандартное поведение. Это существенно влияет на производительность выполнения.
  • Знайте компромиссы производительности/функциональности контейнеров STL (например, не часто вставляйте их в вектор, используйте список).
  • Не инициализируйте переменные, объекты, контейнеры и т.д., когда они будут безоговорочно назначены.
  • Рассмотрим порядок оценки составных условных чисел. Например, если задано if (a && b), если b более вероятно, будет ложным, поместите его сначала, чтобы сохранить оценку.

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

Ответ 5

" Оптимизация программного обеспечения на С++ Agner Fog, как правило, является одной из лучших ссылок на методы оптимизации как простой, так и более продвинутой. Еще одно большое преимущество заключается в том, что его можно свободно читать на своем веб-сайте. (См. Ссылку на его имя для своего сайта и ссылку на бумажное название для pdf).

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

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

Ответ 6

Использовать функторы (классы с operator() реализованы) вместо указателей на функции. У компилятора есть более легкая работа, вложенная в первую. Поэтому С++ std::sort имеет тенденцию работать лучше (при задании функтора), чем C qsort.

Ответ 7

По вашему вопросу, похоже, вы уже знаете о философии "преждевременной оптимизации зла", поэтому я не буду об этом проповедовать.:)

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

Для небольших "оптимизаций" вы можете безопасно и без раздумий, и это не влияет на читаемость/переносимость кода, проверьте раздел "Преждевременная пессимизация" в книге "Стандарты кодирования С++" Sutter и Alexandrescu.

Для получения дополнительных методов оптимизации ознакомьтесь с Efficient С++ от Bulka и Mayhew. Используйте только, если это оправдано профилированием!

Для хороших общих правил программирования на С++, проверьте:

  • Стандарты кодирования С++ от Sutter и Alexandrescu (должно быть, IMHO)
  • Эффективная серия С++/STL от Scott Meyers
  • Исключительная серия С++ от Herb Sutter

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

// Not a good idea, a whole other temporary copy of the (potentially big) vector will be created.
int sum(std::vector<int> v)
{
   // sum all values of v
   return sum;
}

// Better, vector is passed by constant reference
int sum(const std::vector<int>& v)
{
   // v is immutable ("read-only") in this context
   // sum all values of v.
   return sum;
}

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

Когда речь идет о объектах с фиксированным размером и средним весом, это не так ясно, если функция будет работать быстрее с копией или ссылкой на объект. Скажет только профилирование. Обычно я просто передаю ссылку const (если функции не нужна локальная копия) и беспокоиться об этом, если профилирование говорит мне.

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

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

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

Ответ 8

Вот хорошая статья по теме: How To Go Slow

Ответ 9

Использовать правильный контейнер

Контейнеры последовательности

  • Не используйте vector для данных неизвестного размера, если вы собираетесь добавлять к нему данные. Если вы собираетесь повторно вызвать push_back(), используйте reserve() или используйте вместо него deque.
  • Если вы собираетесь добавлять/удалять данные в середине контейнера, list, вероятно, правильный выбор.
  • Если вы собираетесь добавлять/удалять данные с обоих концов контейнера, возможно, правильный выбор. deque.
  • Если вам нужно получить доступ к n-му элементу контейнера, list, вероятно, является неправильным выбором.
  • Если вам нужно как получить доступ к n-му элементу контейнера, так и добавить/удалить элементы в середине, отметьте все три контейнера.
  • Если у вас есть возможность С++ 0x и используете list, но вы никогда не перемещаетесь назад по списку, вы можете найти forward_list больше по своему вкусу. Это не будет быстрее, но это займет меньше места.

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

Ассоциативные контейнеры

  • Если у вас нет TR1, С++ 0x или специфического для поставщика unordered_foo/hash_foo, то нет выбора. Используйте любой из четырех контейнеров, соответствующих вашим потребностям.
  • Если у вас есть unordered_foo, используйте его вместо упорядоченной версии, если вам не нужен порядок элементов, и у вас есть хорошая хэш-функция для этого типа.

Использовать исключения разумно

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

Шаблоны любви

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

Избегайте dynamic_cast

  • dynamic_cast иногда является единственным выбором для чего-то, но часто использование dynamic_cast может быть устранено путем улучшения дизайна.
  • Не заменяйте dynamic_cast на typeid, а затем на static_cast.

Ответ 10

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

Рассмотрим следующее:

#include <string>
using std::basic_string;

template <class T>
    void CreateString(basic_string<T> s)
    {
        //...
    }

Базовая_строка может состоять из char, wchar_t, unsigned char или unsigned wchar_t.

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

Ответ 11

Если вы действительно уверены, что другой тип контейнера лучше, используйте 'std::vector'. Даже если'std:: deque,'std:: list ','std:: map' и т.д. Кажется более удобным выбором, вектор превосходит их как в использовании памяти, так и в доступе к элементу\итерации.

Кроме того, предпочтительнее использовать алгоритм-член контейнера (например, файл apap.equal_range (...) ') вместо своих глобальных сопоставлений ('std:: equal_range (begin(), end()...)')

Ответ 12

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

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

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

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

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

Ответ 13

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

Ответ 14

Вот список, о котором я упомянул в прошлом - http://www.devx.com/cplus/Article/16328/0/page/1. Кроме того, советы по производительности для Googling С++ дают совсем немного.

Ответ 15

Я привык предпочитать писать ++i, а не i++, а не то, что он повышает производительность, когда i является int, но все меняется, когда i является iterator, который может иметь сложная реализация.

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

Кроме того, другой ресурс Стандарты кодирования С++: 101 Правила, рекомендации и рекомендации от Herb Sutter (его снова) и Алексей Александреску.

Существует также более современное издание "Эффективный С++" Скотта Мейерса: Эффективный С++: 55 конкретных способов улучшить ваши программы и проекты.

Наконец, я хотел бы упомянуть Tony Albrecht Ловушки объектно-ориентированного программирования: не то, что в нем содержатся эмпирические правила, вы можете следовать слепо но это очень интересно читать.

Ответ 16

Я предложил бы прочитать главу II ( "Производительность" ) "Программирование жемчуга" от Джона Бентли. Это не С++, но эти методы могут быть применены и на C или С++. На сайте есть только части из книги, я рекомендую прочитать книгу.

Ответ 17

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

Это немного устарело, поэтому вы также должны знать, какие оптимизации уже выполняются вашим компилятором (например, NRVO).

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

Ответ 18

Здесь уже много хороших предложений.

Один из лучших способов проникнуться хорошими привычками - заставить их на себя. Для этого я люблю PC-Lint. PC-Lint фактически обеспечит эффективные С++ и более эффективные правила С++ Скотта Мейера. Также соблюдение правил Lint имеет тенденцию приводить к упрощению обслуживания, снижению подверженности ошибкам и более чистому коду. Просто не сходите с ума, когда вы понимаете, что lint часто генерирует больше выходных данных, чем исходный код; Я когда-то работал над проектом с 150 МБ исходного кода и 1,8 ГБ сообщений Lint.

Ответ 19

  • Избегайте фрагментации памяти.
  • Совместимая память.
  • Инструкции SIMD.
  • Бесключевая многопоточность.
  • Используйте правильные деревья ускорения, такие как kd-tree, tree tree, octree, quadtree и т.д. 5а. Определите их способами, которые допускают первые три (т.е. Делают узлы все в одном блоке).
  • встраивание. Самые низкие висячие, но довольно вкусные фрукты.

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

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

Ответ 20

Предпочитает использовать предварительный приращение.

С int/pointer и т.д. это не имеет значения.
Но с типами классов стандартный способ реализации требует создания нового объекта.

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

Ответ 21

Лучший способ улучшить эти навыки - читать книги и статьи, но я могу помочь вам с некоторыми советами:

  • 1- Принять объекты по ссылке и примитивным или указательным типам по значению, но использовать указатель объекта, если функция сохраняет ссылку или указатель на объект.
  • 2- Не используйте MACROS для объявления констант → используйте static const.
  • 3 Всегда выполняйте виртуальный деструктор, если ваш класс может быть подклассом.

Ответ 22

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

Ответ 23

Почему никто не упоминал об этом до сих пор? Почему каждый человек в бедном маленьком ++i?

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

Эффективный С++ Скотт Мейерс, позиция 20:

Предпочитать значение pass-by-reference-to-const для значения pass-on

Пример:

// this is a better option
void some_function(const std::string &str);
// than this:
void some_function(std::string str);

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

Ответ 24

У этого будет очень хорошие методы для общей оптимизации С++:

http://www.tantalon.com/pete/cppopt/main.htm

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

Я использовал valgrind с инструментом callgrind для цели профилирования, и он даст вам, какие строки стоят сколько.

valgrind --tool = callgrind