Глупый вопрос про шаблоны
От: Zenden Россия  
Дата: 09.09.15 16:26
Оценка:
Вот предположим я пишу

std::vector<size_t> numbers;
numbers.push_back(5);

std::vector<void*> pointers;
pointers.push_back(nullptr);

std::vector<double> doubles;
doubles.push_back(3.14);



Значит ли это, что компилятор генерит на каждый тип копию класса vector?

Вот например size_t и void* имеют одинаковые размеры. Значит классы должны быть как две капли воды одинаковые. Можно их объединить в один.
Да и остальных типов, даже составных отличается только размер и адрес конструктора и деструктора.
Не проще ли оставить один класс а все остальное считать в рантайме?
Re: Глупый вопрос про шаблоны
От: watchmaker  
Дата: 09.09.15 16:53
Оценка: +1
Здравствуйте, Zenden, Вы писали:


Z>Значит ли это, что компилятор генерит на каждый тип копию класса vector?

Да.

Z>Вот например size_t и void* имеют одинаковые размеры.

Размер — это ещё не всё. Например, на x86 uint64_t и double имеют одинаковый размер, но перемещаются они обычно разными семействами инструкций, иначе возможные некоторые нехорошие эффекты (как минимум просадка производительности). Хотя от самих значений ни там, ни там зависимости нет — в обоих случаях просто копируются 64 бита.

Z> Значит классы должны быть как две капли воды одинаковые. Можно их объединить в один.

Что-то такое и так уже присутствует.
Некоторые компиляторы умеют объединять функции с одинаковым кодом (как с финальным скомпилированным, так и с промежуточным во внутреннем представлении).
В некоторых библиотеках построена иерархия классов для типов вроде vector<T>, когда базовый класс реализует всю логику, которая не зависит от T, его наследник реализует другую часть логики, которая зависит только от размера/аллокатора T, а в свою очередь его наследник реализует оставшуюся логику, которая зависит от внутренностей T. И в конце vector является лишь наследником всей этой иерархии. В этом случае общая часть автоматически не дублируется у vector<T> и vector<U>. Но такой код сложнее писать и поддерживать.


Z>Не проще ли оставить один класс а все остальное считать в рантайме?

Во-первых, нет, не проще. Если считаешь иначе, то попробуй рассмотреть что-то хоть чуточку сложнее массива: например, задачу сортировки deque<T> через интерфейс qsort. Через sort(deque.begin(), deque.end()) это получается элементарно.
Во-вторых, на текущем движке шаблонов у тебя и так есть возможность сделать такое поведение. Даже в стандартной библиотеке есть подобные примеры: например, компаратор в std::map может быть как задан через шаблонные параметр, так и передан в runtime через explicit конструктор — в этом случае одинаковый код std::map как раз может использовать различные компрараторы для различных коллекций.
Re: Глупый вопрос про шаблоны
От: SaZ  
Дата: 09.09.15 16:55
Оценка:
Здравствуйте, Zenden, Вы писали:

Z>Значит ли это, что компилятор генерит на каждый тип копию класса vector?

Это нормально.

Z>Не проще ли оставить один класс а все остальное считать в рантайме?

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

А у вас планируется так много различных типов данных, что вы беспокоитесь об этом?
Re[2]: Глупый вопрос про шаблоны
От: Zenden Россия  
Дата: 09.09.15 17:06
Оценка: :)
Здравствуйте, SaZ, Вы писали:

SaZ>А у вас планируется так много различных типов данных, что вы беспокоитесь об этом?


Меня интересует, какой создается оверхед от шаблонов в типичном C++ приложении. Где создается куча всяких однотипных бесполезны классов, типо std::shared_ptr
Вот говорят что от буста код пухнет. Но это ведь не дело. Ведь C++ славится своей эффективностью.
Отредактировано 09.09.2015 17:07 Zenden . Предыдущая версия .
Re: Глупый вопрос про шаблоны
От: Кодт Россия  
Дата: 09.09.15 17:14
Оценка: 6 (2)
Здравствуйте, Zenden, Вы писали:

Z>Не проще ли оставить один класс а все остальное считать в рантайме?


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

А вообще, да, такую оптимизацию иногда делают — но не на уровне компилятора, а на уровне библиотеки.
Например, у семейства set, map, multiset, multimap одинаковый код навигации по дереву.
Поэтому шаблон класса map<Key,Value,Etc...> унаследован от общего для всех них шаблона класса _SomeTree<ItemType>, который, в свою очередь, унаследован от нешаблонного _SomeTreeBase.

Второй способ оптимизации — это выделить важные признаки (к примеру, размер и выравнивание, а также POD-или-не-POD) и использовать из шаблона <T> код (через наследование или вызовы внешних функций) специализированный только для них.
Поскольку vector<T,A> использует аллокатор, специфичный для типа T, — то вот так напрямую этот фокус не пройдёт. Однако std::allocator<T> вполне может обращаться к функциям, специализированным только признаками.

Но это делают только тогда, когда профилирование показало, что кодогенерация является узким местом. (Слишком долгая компиляция, слишком много памяти для компилятора, слишком большой объём объектного кода, слишком много кешмиссов на страницах кода при работе программы).
Обычно, всё-таки, программа большого размера без рантаймовых проверок работает быстрее, чем компактная программа с проверками на каждый чих.
Перекуём баги на фичи!
Re[3]: Глупый вопрос про шаблоны
От: watchmaker  
Дата: 09.09.15 17:24
Оценка: +4
Здравствуйте, Zenden, Вы писали:

Z>Вот говорят что от буста код пухнет. Но это ведь не дело. Ведь C++ славится своей эффективностью.

Эффективность бывает разная: есть скорость работы, есть объём программы, есть удобство написания кода, в конце-концов.
Идеал, максимизирующий всё это, недостижим. Но можно смещать баланс в ту или иную сторону (даже, например, на уровне опций компилятора -Os, -O2, -O3, которые обычно одновременно как увеличивают скорость, так и увеличивают объём кода).
Шаблоны в среднем способны дать более быстрый код ценой возможного создания семейства похожих функций. Динамический полиморфизм, наоборот, даёт возможность обойтись небольшим числом функций, но взамен чуть более медленной работой из-за тех самых проверок в run-time. С++ славится тем, что даёт тебе самому решить что для тебя более важно :)
Re[3]: Глупый вопрос про шаблоны
От: Кодт Россия  
Дата: 09.09.15 17:49
Оценка:
Здравствуйте, Zenden, Вы писали:

Z>Меня интересует, какой создается оверхед от шаблонов в типичном C++ приложении. Где создается куча всяких однотипных бесполезны классов, типо std::shared_ptr

Z>Вот говорят что от буста код пухнет. Но это ведь не дело. Ведь C++ славится своей эффективностью.

Во-первых, много чего инлайнится.
Так что типизированный фасад shared_ptr над его потрохами — это беда только в -Od.

Во-вторых, любая достаточно сложная библиотека — её автор сам определяет для себя, будет он гнаться за скоростью, размером или простотой.

Стандартная библиотека — это компромиссное решение. Можно сделать проще и тормозливее, можно повыжимать все соки и написать своё скоростное под конкретную задачу.
Мой любимый пример таких контрастов — это реализации BLAS под С++. boost::ublas простой, но туууупой. А Eigen хитровывернутый, там именно на шаблонах и на их специализации творятся чудеса оптимизации. (Отлаживать недра — замучаешься).

И пример неудачной оптимизации — это Google OpenFST. Там половина кода — насильственно превращена из рантаймового в компиляторный полиморфизм ("шоб инлайнилось"), а другая половина — из компиляторного в рантаймовый ("шоб не пухло"). В большей части мест это даёт позитивный эффект, но в некоторых — ад, боль и унижение. Там и код пухнет, и тормоза в рантайм проникают, и отлаживать замучаешься.
Перекуём баги на фичи!
Re[3]: Глупый вопрос про шаблоны
От: Ops Россия  
Дата: 09.09.15 19:29
Оценка:
Здравствуйте, Zenden, Вы писали:

Z>Меня интересует, какой создается оверхед от шаблонов в типичном C++ приложении. Где создается куча всяких однотипных бесполезны классов, типо std::shared_ptr

Никаких классов не создается. Создаются методы, причем только используемые фактически, если вызова нигде нет, то и кода не будет. А дальше компилятор может объединять одинаковые реализации. В результате памяти может уходить меньше, чем при динамическом полиморфизме, за счет отсутствия vtable, кода неиспользуемых методов (его часто нельзя выкинуть, лишь иногда при LTCG, а с шаблонами он просто не будет генерироваться) и лишней косвенности. Это не говоря о том, что код часто инлайнится, уменьшая оверхед на вызов функции.
Z>Вот говорят что от буста код пухнет. Но это ведь не дело. Ведь C++ славится своей эффективностью.
Говорят что кур доят. Буст это не одна монолитная библиотека, а много совершенно разных, причем не все они на шаблонах, и не все вообще создают код.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re: Глупый вопрос про шаблоны
От: Molchalnik  
Дата: 10.09.15 15:17
Оценка:
Здравствуйте, Zenden, Вы писали:




Z>Значит ли это, что компилятор генерит на каждый тип копию класса vector?


Z>Вот например size_t и void* имеют одинаковые размеры. Значит классы должны быть как две капли воды одинаковые. Можно их объединить в один.

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

Z>Да и остальных типов, даже составных отличается только размер и адрес конструктора и деструктора.

Z>Не проще ли оставить один класс а все остальное считать в рантайме?
а смысл тогда в шаблонах? плюс шаблонов — скорость. а в рантайме считают другие языки, в основном скрипты.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.