Значительно ли увеличивает использование STL? - PullRequest
12 голосов
/ 15 декабря 2008

Значительно ли использование STL увеличивает площадь присутствия? Не могли бы вы, ребята, поделиться своим опытом по этому вопросу? Каковы лучшие практики для создания небольшой библиотеки занимаемых площадей?

Ответы [ 8 ]

20 голосов
/ 15 декабря 2008

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

10 голосов
/ 15 декабря 2008

Дело в 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
4 голосов
/ 15 декабря 2008

Использование STL увеличит ваш двоичный размер по двум основным причинам:

Встраивание

По умолчанию компилятор обрабатывает код шаблона как встроенный. Поэтому, если вы используете std::list<int> в нескольких различных единицах компиляции, и компилятор вставляет этот код , у каждого из них будут свои собственные локальные встроенные определения функций std::list<int> (что, вероятно, не является большим дело, так как компилятор будет вставлять только очень маленькие определения по умолчанию).

Обратите внимание, что (как указывает Мартин в другом месте) многократно определенные символы удаляются всеми современными компоновщиками C ++ на больших платформах, как описано в документации GCC . Таким образом, если компилятор оставляет код шаблона вне строки, компоновщик удаляет дубликаты.

Специализация

Из-за самой природы шаблонов C ++, std::list<int> потенциально произвольно отличается от std::list<double>. Фактически, стандартный мандат определяет, что std::vector<bool> определяется как битовый вектор, поэтому большинство операций там полностью отличается от значения по умолчанию std::vector<T>.

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

2 голосов
/ 15 декабря 2008

Хотя это не относится к шаблонам STL, в документации для GCC есть короткий раздел по минимизации раздувания кода при использовании шаблонов шаблонов.

Ссылка http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Template-Instantiation.html#Template-Instantiation

2 голосов
/ 15 декабря 2008

Я предполагаю, что вы имеете в виду время выполнения память занимаемая площадь, и, следовательно, STL контейнеры .

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

Некоторые хорошие практики:

  • Если ваша библиотека собирается предоставлять эти контейнеры через API, вам, возможно, придется выбирать между размещением кода STL в заголовках библиотеки или без использования STL. Проблема в том, что мой компилятор не должен реализовывать STL так же, как ваш.
  • Читайте о том, как и когда STL контейнеры выделяют память. когда Вы можете визуализировать, как растет deque и сжимается по сравнению с вектором, Вы будете лучше подготовлены, чтобы решить, какой использовать.
  • Если вам нужно микроуправлять каждым байтом, подумайте о написании пользовательских распределителей. Это редко необходимо вне встроенных систем.
1 голос
/ 15 декабря 2008

Специализации STL, основанные на указателях, могут использовать одну и ту же реализацию. Это потому, что (void *) имеет тот же размер, что и (int *) или (foo *). Итак, пока:

вектор & языки; INT & зазвонил; и вектор & lang; foo & rang; это разные реализации.

vector & lang; int * & rang; и вектор & lang; foo * & rang; может иметь большую часть той же реализации. Во многих реализациях STL это делается для экономии памяти.

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

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

1 голос
/ 15 декабря 2008

Игнорируя обсуждение STL на данный момент, существует одна неочевидная важная практика для создания статической библиотеки с небольшим пространством. Разбейте свою библиотеку на как можно больше разрозненных модулей компиляции. Например, если вы посмотрите на libpthread.a, вы увидите, что каждая функция имеет свой собственный модуль компиляции. Многие линкеры выбрасывают мертвый код, основанный на единицах компиляции, но не более детальный, чем этот. Если я использую только несколько функций из библиотеки pthreads, мой компоновщик будет вводить только эти определения и ничего больше. С другой стороны, если бы вся библиотека была скомпилирована в один объектный файл, мой компоновщик должен был бы привести всю библиотеку как единый «модуль».

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

0 голосов
/ 15 декабря 2008

Во встроенном проекте с ограничением в 64 КБ, которое я когда-то делал, я не мог даже связать стандартные библиотеки C. Так что это зависит от того, что вам нужно сделать

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