Вопросы о производительности - PullRequest
2 голосов
/ 13 сентября 2010

У меня было несколько вопросов об использовании встроенных функций на C и C ++.Мне сказали использовать его в небольших функциях, которые я часто использую, но я хочу понять, как именно это работает.Вот только фрагмент примера.

static inline point3D createPoint3D(float x, float y, float z){
   point3D newPosition;
   newPosition.x = x;
   newPosition.y = y;
   newPosition.z = z;
   return newPosition;
}
  1. Что именно он делает и почему он помогает коду работать быстрее?Является ли это устаревшей оптимизацией 90-х?

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

  3. Разве это плохо для большого числа функций?

Ответы [ 4 ]

4 голосов
/ 13 сентября 2010
  1. Это больше похоже на устаревшую оптимизацию 70-х или (максимум) 80-х.Практически любой компетентный компилятор может выбирать функции для встроенного расширения без какой-либо помощи с вашей стороны, кроме того, что позволяет начать оптимизацию.

  2. То, что он должен делать, - это исключить накладные расходы на вызов функции.Это главным образом важно для таких вещей, как крошечные функции, которые делают практически ничего.Как это случается, они достаточно распространены, так что для достижения даже половинной приличной производительности из C ++ почти требуется, чтобы компилятор расширял функции встроенным более или менее автоматически.

  3. Обычно вообще бессмысленно использовать его.

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

Следует отметить две вещи: 1) большинство компиляторов могут / будут генерировать встроенные функции без ключевого слова inline, и 2) большинство компиляторов могут / будут игнорировать ключевое слово inline, если они считают, что функция не подходит для встроенного расширения (хотя, только FWIWУ Microsoft есть __forceinline, чтобы преодолеть последнее, если вы действительно уверены, что знаете лучше, чем компилятор).

4 голосов
/ 13 сентября 2010

Пожалуйста, смотрите эту подробную информацию в C ++ FAQ здесь .Чтобы процитировать это встроенные функции ..

Когда встроенный компилятор вызывает вызов функции, код функции вставляется в поток кода вызывающей стороны (концептуально аналогично тому, что происходит с макросом #define).Это может, в зависимости от множества других факторов, повысить производительность, поскольку оптимизатор может процедурно интегрировать вызываемый код - оптимизировать вызываемый код в вызывающую программу.

Раздел 9.3

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

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

встроенные функции могут сделать его больше: это понятиеРаздувание кода, как описано выше.Например, если система имеет 100 встроенных функций, каждая из которых расширяется до 100 байт исполняемого кода и вызывается в 100 местах, это увеличение на 1 МБ.Это 1 МБ будет вызывать проблемы?Кто знает, но возможно, что последние 1 МБ могут вызвать «сбои» системы, что может замедлить процесс.

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

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

встроенные функции могут предотвращать перебивание: Размер рабочего набора (количество страниц, которое должно быть в памяти приодин раз) может уменьшиться, даже если размер исполняемого файла увеличится.Когда f () вызывает g (), код часто находится на двух разных страницах;когда компилятор процедурно интегрирует код g () в f (), код часто находится на одной и той же странице.

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

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

могут не иметь отношения к скорости: большинство систем не связаны с процессором.Большинство систем привязаны к вводу / выводу, к базе данных или к сети, что означает, что узким местом в общей производительности системы является файловая система, база данных или сеть.Если ваш «измеритель ЦП» не установлен на 100%, встроенные функции, вероятно, не сделают вашу систему быстрее.(Даже в системах с привязкой к ЦП inline поможет только при использовании внутри самого узкого места, а узкое место обычно присутствует в небольшом проценте кода.)

Простых ответов нет: вы должны игратьс ним, чтобы увидеть, что лучше.Не соглашайтесь на упрощенные ответы, такие как «Никогда не используйте встроенные функции» или «Всегда используйте встроенные функции» или «Используйте встроенные функции, если и только если функция меньше N строк кода».Эти правила одного размера могут быть легко записаны, но они приведут к неоптимальным результатам.

3 голосов
/ 13 сентября 2010

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

1) inline - это предложение компилятору "встроить" функцию непосредственно в поток кода, а не "вызывать" ее.Это позволяет обойти необходимость установки стека и выполнять другие обязанности, необходимые для вызова функции

        NOT INLINE                    INLINE
        ...                           ...
        code                          code
        call fx    -\                 code from fx
        code        |                 code from fx
        call fx   --|                 code from fx
        ...         |                 code
                    |                 code from fx
        code <------/                 code from fx
        ...                           code from fx
        return                        ...

2) Используйте ее везде, где вам нужно.Компилятор, скорее всего, проигнорирует ваше предложение

3) аналогично 2)

4) мере.экспериментируйте и сравнивайте

2 голосов
/ 13 сентября 2010

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

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

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

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

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

не так уж и много.
...