Являются ли библиотеки только для заголовков более эффективными?

49

Предположения

  1. Одним из преимуществ библиотек только для заголовков для C ++ является то, что их не нужно компилировать отдельно.

  2. В C и C ++ inlineимеет смысл только если функция определена в заголовочном файле *.

  3. Традиционно в C использовался макет .c / .h, где заголовок представляет минимальный открытый интерфейс модуля перевода. Точно так же .cpp / hpp.

Вопрос

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

* - определение функции в заголовке позволяет компилятору видеть реализацию во время компиляции любого модуля перевода и практически делает возможным встраивание кода

Vorac
источник
2
Вы будете удивлены, насколько хорошо многие современные компоновщики C ++ (GCC, MSVC, ICC и т. Д.) Могут встроить код в отдельные блоки перевода. Я бы сказал «вообще нет» с точки зрения эффективности, учитывая, сколько раз оптимизирующие компоновщики бросали вызов моим ожиданиям и в любом случае им удавалось встроить (это исключает контексты dylib, где может помочь встраивание в заголовок или предоставление реализации в отдельной статически связанной библиотеке) , Тем не менее, библиотеки только для заголовков, при условии, что они стабильны как в интерфейсе, так и в реализации, могут быть привлекательными из-за того, как легко их можно развертывать в новых проектах.
1
Я не знаю, что это заслуживает полного ответа, но одно огромное преимущество только заголовков - простота установки и использования: загрузка, #include "lib.h (pp)", готово.
WeRelic

Ответы:

45

Одним из преимуществ библиотек только для заголовков для C ++ является то, что их не нужно компилировать отдельно

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

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

Это зависит от системы компилятор / компоновщик, но я думаю, что для большинства существующих компиляторов C и C ++ это действительно так.

Традиционно в C использовался макет .c / .h, где заголовок представляет минимальный открытый интерфейс модуля перевода. Точно так же .cpp / hpp.

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

Но чтобы ответить на ваш главный вопрос: это компромисс. Чем больше библиотечного кода помещается в заголовочные файлы, тем больше у компилятора шансов оптимизировать код по скорости (если это действительно произойдет, или если увеличение заметно, это совершенно другой вопрос). С другой стороны, слишком большое количество кода в заголовках увеличивает время компиляции. Особенно в больших проектах на C ++ это может стать серьезной проблемой, см. «Разработка программного обеспечения для крупномасштабного C ++» Джона Лакоса - хотя книга немного устарела и некоторые из описанных здесь проблем решаются современными компиляторами, общие идеи / решения остаются в силе.

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

ИМХО популярность библиотек только с заголовками обусловлена ​​популярностью шаблонного метапрограммирования. Для большинства компиляторов шаблонные библиотеки должны иметь только заголовок, потому что компилятор может запустить основной процесс компиляции только при условии указания параметров типа, а для полной компиляции и оптимизации компилятор должен видеть «оба сразу» - код библиотеки плюс шаблон значения параметров. Это делает невозможным (или, по крайней мере, трудным) создание каких-либо «предварительно скомпилированных» модулей компиляции для такой библиотеки.

Док Браун
источник
6
Короче говоря, библиотеки только для заголовков более удобны , чем эффективны ; и так как C ++ не имеет никакого стандартного менеджера пакетов, это помогает в принятии.
Матье М.
6
@MatthieuM: нет, скомпилированный код действительно иногда может быть более эффективным, и для шаблонных библиотек дизайн только с заголовками обычно не является вопросом удобства. И увеличенное время компиляции определенно не более удобно.
Док Браун
Наличие горячего кода в заголовках действительно может привести к более эффективному скомпилированному коду, однако это не требует наличия всего кода в заголовках и, следовательно, является независимым, IMHO, от библиотек только для заголовков.
Матье М.
1
@MatthieuM .: это, безусловно, правильно, тем не менее, я думаю, что термин «более удобный» не очень хорошо описывает случай.
Док Браун
3
Я работал над большим встроенным проектом поверх урезанной, старой ОС для моего предыдущего работодателя, и в этих случаях я могу засвидетельствовать боль долгого времени компиляции. Любой, кто поймал слишком много в заголовочных файлах, будет безжалостно расправляться.
Фред Томсен
15

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

  1. Одним из преимуществ библиотек только для заголовков для C ++ является то, что их не нужно компилировать отдельно.

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

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

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

Итак, если функция является внутренней по отношению к модулю компиляции, отметьте ее static. Это также делает встраивание более вероятным, так как функция должна быть доступна для его встраивания.
Тем не менее, взгляните на оптимизацию времени соединения, поддерживаемую по крайней мере MSVC ++, gcc и clang.

  1. Традиционно в C использовался макет .c / .h, где заголовок представляет минимальный открытый интерфейс модуля перевода. Точно так же .cpp / hpp.

Что ж, только представление минимального интерфейса, безусловно, является одной из целей - добиться более высокой стабильности API и ABI и минимизировать время компиляции.

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

Дизайн-шаблон PIMPL предназначен для уменьшения таких деталей.

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

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

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

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

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

Deduplicator
источник