Есть ли способ предотвратить обработку заголовочной функции c ++ как встроенной? - PullRequest
1 голос
/ 07 декабря 2009

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

"Даже встроенные функции (отличные от тех, которые требуются классами) - дискуссионная практика. Теоретически он создает более быстрый, но более крупный код (избегает вызовов функций и возвращает дублирующийся код). Однако некоторые люди заметили то, что часто использование in-lining фактически создает более медленный код. Причина в том, что это может привести к тому, что код станет больше и превысит размер того, что помещается в один или несколько кэшей, используемых во время выполнения. В результате он вызывает части функция, позволяющая входить и выходить из кеша при каждом прохождении некоторого цикла, пропуски кеша и последующие перезагрузки обходятся гораздо дороже, чем вызов функции для чего-то, уже находящегося на другой странице кеша. Это интересная ситуация, которую нельзя предсказать, только наблюдается методом проб и ошибок. "

Ответы [ 10 ]

24 голосов
/ 07 декабря 2009

Ваш коллега должен проверить значение встраивания в C ++.

Есть два значения этого слова, и важно их разделить:

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

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

Затем идет "inline" оптимизация , которая состоит из взятия тела функции и вставки его вместо вызова функции.

Эти значения почти полностью ортогональны. Функция может быть встроена компилятором независимо от того, отметил ли вы, программист, что это inline. (Хотя компилятору труднее и реже иметь возможность встроить, если функция вызывается в другом модуле перевода, чем тот, в котором она была определена) Функция, помеченная inline в C ++, может или не может быть встроена компилятор. Компилятор пытается оценить возможную выгоду от этого, основываясь на размере кода, частоте вызова функции, количестве сайтов вызовов и такой эвристике. В результате компилятор довольно хорошо определяет, когда стоит встраивать оптимизация , и вам лучше всего оставить его в покое.

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

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

6 голосов
/ 07 декабря 2009

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

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


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

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

2 голосов
/ 07 декабря 2009

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

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

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

Так что вам придется взвесить все за и против, но встраивание не является одним из минусов.

1 голос
/ 07 декабря 2009

Если функция является членом класса, объявите функцию виртуальной.

1 голос
/ 07 декабря 2009

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

1 голос
/ 07 декабря 2009

Visual Studio поддерживает

__declspec(noinline)

см. http://msdn.microsoft.com/en-us/library/kxybs02x%28VS.80%29.aspx

Возможно, другие компиляторы имеют аналогичные конструкции.

0 голосов
/ 08 декабря 2009

Просто отвечаю на это:

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

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

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

Здесь есть урок, и вы должны внимательно следить за тем, что ваш компилятор делает с высокопроизводительным кодом. Но для остальных 80% вашего кода компилятор, как правило, сам делает правильные вещи.

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

0 голосов
/ 08 декабря 2009

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

  • Встроенный код принудительно перекомпилирует всех пользователей библиотеки, если этот код изменяется.
  • Связывание кода C с библиотекой C ++ с экспортированными функциями C и минимальным заголовком C становится невозможным, поскольку конечный результат не содержит встроенный код.
  • Был также некоторый беспорядок с информацией о типе, который я не могу вспомнить все детали.
0 голосов
/ 08 декабря 2009

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

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

0 голосов
/ 07 декабря 2009

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

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