Значительно ли увеличивает использование STL?
Значительно ли увеличивает использование STL? Не могли бы вы, ребята, поделиться своим опытом по этому вопросу? Каковы наилучшие практики для создания небольшой библиотеки?
8 ответов
Единого ответа нет, так как STL - это набор шаблонов. Шаблоны по самой своей природе компилируются только при использовании. Таким образом, вы можете включить все STL, и если ни один из них фактически не используется, занимаемая площадь, добавленная STL, будет равна нулю. Если у вас очень маленькое приложение, которое может использовать множество различных шаблонов с различными специализациями, занимаемая площадь может быть большой.
Дело в STL, потому что это все шаблоны, которые он только добавляет к размеру, когда вы на самом деле используете. Если вы не используете метод, то этот метод не создается.
Но всегда будет цена за вещи, которые вы используете.
Но реальный вопрос вы должны задать. Будет ли размер больше или меньше вашей собственной реализации? Если вы не используете STL, что вы используете? Вы можете написать свои собственные, но это имеет свою цену. Это не нулевое пространство, оно не будет хорошо протестировано, и вы не будете следовать установленным лучшим практикам.
Так что на самом деле нет, это не раздувает ваш код.
Поскольку добавление эквивалентной функциональности каким-либо другим методом добавит столько же кода, оно будет не так хорошо протестировано.
В другом сообщении говорится, что код шаблона должен быть встроенным или иметь несколько определений.
Это абсолютно НЕПРАВИЛЬНО.
Методы помечены как встроенные. Но это зависит от компилятора, если метод на самом деле встроен или нет. Встраивание компилятора достаточно сложное, чтобы встроить его, только если это поможет в использовании стратегии оптимизации.
Если не встроено, тогда копия метода будет сгенерирована в каждой единице компиляции, которая использует метод. Но ТРЕБОВАНИЕ для компоновщика C++ заключается в том, что он должен удалять ВСЕ, но ОДИН экземпляр этих методов, когда приложение связано в исполняемый файл. Если компилятор не удалит лишние копии, он должен будет сгенерировать ошибку компоновщика с несколькими определениями.
Это легко показать:
Следующее иллюстрирует это:
- Метод _M_insert_aux() не является встроенным.
- Он размещается в обеих единицах компиляции.
- Эта только одна копия метода находится в конечном исполняемом файле.
a.cpp
#include <vector>
void a(std::vector<int>& l)
{
l.push_back(1);
l.at(0) = 2;
}
b.cpp
#include <vector>
void b(std::vector<int>& l)
{
l.push_back(1);
l.at(0) = 2;
}
main.cpp
#include <vector>
void a(std::vector<int>&);
void b(std::vector<int>&);
int main()
{
std::vector<int> x;
a(x);
b(x);
}
проверка
>g++ -c a.cpp
>g++ -c b.cpp
>nm a.o
<removed other stuff>
000000a0 S __ZNSt6vectorIiSaIiEE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPiS1_EERKi
<removed other stuff>
>nm b.o
<removed other stuff>
000000a0 S __ZNSt6vectorIiSaIiEE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPiS1_EERKi
<removed other stuff>
>c++filt __ZNSt6vectorIiSaIiEE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPiS1_EERKi
std::vector<int, std::allocator<int> >::_M_insert_aux(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&)
>g++ a.o b.o main.cpp
nm a.out | grep __ZNSt6vectorIiSaIiEE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPiS1_EERKi
00001700 T __ZNSt6vectorIiSaIiEE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPiS1_EERKi
Использование STL увеличит ваш двоичный размер по двум основным причинам:
Встраивание
По умолчанию компилятор обрабатывает код шаблона как встроенный. Поэтому, если вы используете std::list<int>
в нескольких различных модулях компиляции, и компилятор вставляет этот код, каждый из них будет иметь свои собственные локальные встроенные определения std::list<int>
функции (что, вероятно, не имеет большого значения, так как по умолчанию компилятор будет вставлять только очень маленькие определения).
Обратите внимание, что (как указывает Мартин в другом месте) многократно определенные символы удаляются всеми современными компоновщиками C++ на больших платформах, как описано в документации GCC. Таким образом, если компилятор оставляет код шаблона вне строки, компоновщик удаляет дубликаты.
специализация
Из-за самой природы шаблонов C++, std::list<int>
потенциально произвольно отличается от std::list<double>
, На самом деле, стандартные мандаты, которые std::vector<bool>
определяется как бит-вектор, поэтому большинство операций там полностью отличаются от стандартных std::vector<T>
,
Только сопровождающий библиотеки может иметь дело с этим. Одно из решений - взять базовую функциональность и "не шаблонизировать" ее. Превратите его в структуру данных в стиле C с void*
везде. Тогда интерфейс шаблона, который видят разработчики из нижестоящего потока, является тонкой оболочкой. Сокращается количество дублирующегося кода, поскольку специализации шаблонов имеют общую основу.
Я предполагаю, что вы имеете в виду объем оперативной памяти и, следовательно, контейнеры STL.
Контейнеры STL эффективны тем, чем они являются... контейнерами общего назначения. Если вы решаете между написанием собственного двусвязного списка или использованием std::list, пожалуйста... используйте STL. Если вы планируете писать очень специфичные для домена битовые контейнеры для каждой конкретной задачи, сначала используйте STL, а затем выбирайте битвы, как только весь ваш код работает правильно.
Некоторые хорошие практики:
- Если ваша библиотека собирается предоставлять эти контейнеры через API, вам, возможно, придется выбирать между размещением кода STL в заголовках библиотеки или без использования STL. Проблема в том, что мой компилятор не должен реализовывать STL так же, как ваш.
- Узнайте, как и когда контейнеры STL выделяют память. Когда вы сможете визуализировать, как растёт и уменьшается декка по сравнению с вектором, вы будете лучше подготовлены к решению, какой из них использовать.
- Если вам нужно микро-управлять каждым байтом, подумайте о написании пользовательских распределителей. Это редко необходимо вне встроенных систем.
Хотя это не относится к шаблонам STL, в документации для GCC есть короткий раздел, посвященный минимизации раздувания кода при использовании шаблонов шаблонов.
Ссылка http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Template-Instantiation.html
Специализации STL, основанные на указателях, могут иметь одинаковую реализацию. Это потому что (void *) имеет тот же размер, что и (int *) или (foo *). Так что пока:
vector⟨int⟩ и vector⟨foo⟩ - это разные реализации.
vector⟨int *⟩ и vector⟨foo *⟩ могут использовать одну и ту же реализацию. Многие реализации STL делают это для экономии памяти.
Если весь шаблон определен в заголовке, поэтому он полностью определен, компиляторы, такие как g++, автоматически создадут копию в каждом модуле компиляции, используя класс. Как говорили другие, компоновщик удалит несколько определений.
Они также автоматически встроят методы в определение класса для более высоких уровней оптимизации. Но это можно контролировать с помощью параметров компилятора.
Не обращая внимания на обсуждение STL на данный момент, существует одна неочевидная важная практика для создания статической библиотеки с небольшим пространством. Разбейте свою библиотеку на как можно больше разрозненных модулей компиляции. Например, если вы посмотрите на libpthread.a
вы увидите, что каждая функция имеет свой собственный модуль компиляции. Многие компоновщики будут выбрасывать мертвый код, основанный на единицах компиляции, но не более детализированный, чем этот. Если я использую только несколько функций из библиотеки pthreads, мой компоновщик введет только эти определения и ничего больше. С другой стороны, если бы вся библиотека была скомпилирована в один объектный файл, мой компоновщик должен был бы объединить всю библиотеку как единый "модуль".
Это зависит от используемой вами цепочки инструментов и применяется только при создании статических библиотек. Но я видел, что это имеет очень ощутимое значение для больших библиотек, построенных неоптимально.
Во встроенном проекте с ограничением в 64 КБ, которое я когда-то делал, я не мог даже связать стандартные библиотеки C. Так что это зависит от того, что вам нужно сделать