Какова цель (-ы) встроенного?

У меня была дискуссия с Йоханнесом Шаубом относительно ключевого слова inline. Код был следующим:

namespace ... {
    static void someFunction() {
        MYCLASS::GetInstance()->someFunction();
    }
};

Он заявил, что:

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

Но согласно моим выводам здесь и здесь он не понадобится, поскольку:

  • [Inline] возникает только в том случае, если анализ затрат/выгод от компилятора показывает его прибыльность.
  • Компиляторы Mainstream С++, такие как Microsoft Visual С++ и GCC, поддерживают параметр, который позволяет компиляторам автоматически вводить любую подходящую функцию, даже те, которые не помечены как встроенные функции.

Однако Йоханнес утверждает, что есть другие преимущества явного указания его. К сожалению, я их не понимаю. Например, он заявил, что и "inline" позволяет вам определять функцию несколько раз в программе. У меня с трудом понимается (и нахождение ссылок).

Итак,

  • Является ли inline рекомендацией для компилятора?
  • Должно ли это быть явно указано, когда у вас есть небольшая функция (я думаю, 1-4 инструкции?)
  • Какие еще преимущества существуют при написании inline?
  • необходимо указать inline, чтобы уменьшить размер исполняемого файла, даже если компилятор (согласно wikipedia [я знаю, плохая ссылка]) должен найти такие функции самостоятельно?

Есть ли что-то еще, что мне не хватает?

Ответы

Ответ 1

Является ли встроенным только рекомендация для компилятора?

Да.

7.1.2 Спецификаторы функций

2 Объявление функции (8.3.5, 9.3, 11.4) с встроенным спецификатором объявляет встроенную функцию. Встроенный specifier указывает на реализацию, что встроенная подстановка тела функции в точке вызова является предпочтительным для обычного механизма вызова функций. Реализация не требуется для выполнения этого встроенная подстановка в точке вызова; однако, даже если эта встроенная замена опущена, другие правила для встроенных функций, определенных в 7.1.2, все равно должны соблюдаться.

Например, из MSDN:

Компилятор рассматривает варианты расширения и ключевые слова в качестве предложений. Нет никакой гарантии, что функции будут включены. Вы не можете заставить компилятор встроить определенную функцию даже с ключевым словом __forceinline. При компиляции с /clr компилятор не будет встроить функцию, если к функции применяются атрибуты безопасности.

Обратите внимание:

3.2 Одно правило определения

3 [...] Встроенная функция должна быть определена в каждой единицы перевода, в которой она используется.

4 Встроенная функция должна быть определена в каждой единицы перевода, в которой она используется, и должна иметь точно одно и то же определение в каждом случае (3.2). [Примечание: вызов встроенной функции может быть встречен до определение появляется в блоке перевода. -end note] Если определение функции появляется в переводе блок до его первого объявления как встроенный, программа плохо сформирована. Если функция с внешней связью объявленный inline в одной единицы перевода, он должен быть объявлен встроенным во все единицы перевода, в которых он появляется; никакая диагностика не требуется. Встроенная функция с внешней связью должна иметь одинаковый адрес во всех единицы перевода. Статическая локальная переменная во внешней встроенной функции всегда относится к одному и тому же объекту. Строковый литерал в теле внешней функции inline является одним и тем же объектом в разных единицах перевода. [Примечание: строковый литерал, отображающийся в выражении аргумента по умолчанию, не находится в теле встроенной функции просто потому, что выражение используется в вызове функции из этой встроенной функции. -end note] Тип определяемый внутри тела внешней встроенной функции, является одним и тем же типом в каждой единицы перевода.

[Примечание: акцент мой]

A TU - это, в основном, набор заголовков плюс файл реализации (.cpp), который приводит к объектным файлам.

Если это явно указано, если у вас есть небольшая функция (I угадайте 1-4 инструкции?)

Совершенно верно. Почему бы не помочь компилятору помочь вам генерировать меньше кода? Обычно, если часть пролога /epilog берет на себя большую стоимость, чем наличие встроенного силового компилятора для их создания? Но вы должны абсолютно пройти эту статью GOTW, прежде чем начинать с вложения: GotW # 33: Inline

Какие еще преимущества существуют при написании inline?

  • namespace тоже может быть inline. Обратите внимание, что функции-члены, определенные в самом классе класса, по умолчанию являются встроенными. Таким образом, неявно генерируются специальные функции-члены.

  • Шаблоны функций не могут быть определены в файле реализации (см. FAQ 35.12), если, конечно, вы не указали явные экземпляры (для все типы, для которых используется шаблон - обычно PITA IMO). См. Статью DDJ на Перемещение шаблонов из файлов заголовков (Если вы чувствуете странное прочтение этой другой статьи о ключевое слово export, которое было удалено из стандарта.)

Нужно ли указывать встроенную строку, чтобы уменьшить исполняемый файл размер, хотя компилятор (согласно wikipedia [я знаю, плохо ссылка]) должны найти такие функции сам?

Опять же, как я уже сказал, как хороший программист, вы должны, когда можете, помочь компилятору. Но здесьчто предлагает С++ FAQ о inline. Так что будьте осторожны. Не все компиляторы проводят такой анализ, поэтому вы должны прочитать документацию об их переключателях оптимизации. Например: GCC делает что-то подобное:

Вы также можете направить GCC, чтобы попытаться интегрировать все "простые" функции в своих вызывающих абонентов с опцией -finline-functions.

Большинство компиляторов позволяют в некоторой степени переопределить анализ соотношения затрат и выгод от компилятора. MSDN и GCC документация стоит прочитать.

Ответ 2

Повторить то, что я сказал в этих маленьких блоках комментариев. В частности, я никогда не говорил об этом:

// foo.h:
static void f() {
  // code that can't be inlined
}

// TU1 calls f
// TU2 calls f

Теперь как TU1, так и TU2 имеют свою собственную копию f - код f находится в исполняемом файле два раза.

// foo.h:
inline void f() {
  // code that can't be inlined
}

// TU1 calls f
// TU2 calls f

Оба TU будут выделять специально выделенные версии f, которые эффективно объединяются компоновщиком, отбрасывая все, кроме одного из них. Код f существует только один раз в исполняемом файле.

Таким образом, мы сохранили пространство в исполняемом файле.

Ответ 3

Является ли встроенным только рекомендация для компилятора?

Да. Но компоновщик нуждается в нем, если существует несколько определений функции (см. Ниже)

Должно ли это быть явно указано, когда у вас есть небольшая функция (я думаю, 1-4 инструкции?)

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

Какие еще преимущества существуют при написании inline?

При правильном использовании он остановит ошибки компоновщика.

нужно ли указывать inline для уменьшения размера исполняемого файла, даже если компилятор (согласно wikipedia [я знаю, плохая ссылка]) должен найти такие функции самостоятельно?

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

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

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

Несколько определений:

Файл: head.h

// Without inline the linker will choke.
/*inline*/       int  add(int x, int y) { return x + y; }
extern void test()

Файл: main.cpp

#include "head.h"
#include <iostream>

int main()
{
    std::cout << add(2,3) << std::endl;
    test();
}

Файл: test.cpp

#include "head.h"
#include <iostream>

void test()
{
    std::cout << add(2,3) << std::endl;
}

Здесь мы имеем два определения add(). Один в main.o и один в test.o

Ответ 4

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

Ответ 5

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

Ответ 6

Что касается этого:

И "inline" позволяет вам определять функцию несколько раз в программе.

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

Что касается других пунктов:

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

Ответ 7

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

  • Он должен быть явно указан, если вы поместили определение функции в файл заголовка. Как правило, это необходимо для обеспечения возможности использования нескольких единиц перевода. И избежать ошибок определения. Кроме того, встроенные функции помещаются в раздел COMDAT. Что говорит компоновщику, что он может выбрать только одно из нескольких определений. Эквивалентен __declspec (selectany) в MSVC.

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

Ответ 8

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

Ответ 9

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

http://www.parashift.com/c++-faq-lite/inline-functions.html