Влияние использования рекурсивной реализации шаблона для кортежей на время компиляции ⇐ C++
-
Гость
Влияние использования рекурсивной реализации шаблона для кортежей на время компиляции
Некоторые источники, например, «Почему нехорошо использовать рекурсивное наследование для реализаций std::tuple?», указывают на затраты времени компиляции при использовании рекурсивной реализации шаблона для кортежей. Я подумываю о написании системы, которая будет использовать сотни кортежей, каждый из которых может иметь размер от 1 до 100 (в среднем от 5 до 10, я думаю), и задаюсь вопросом, стоит ли мне заботиться о том, чтобы стандартные библиотеки, которые я буду использовать, имели рекурсивный подход. выполнение? Может ли кто-нибудь оценить это количественно? влияние на время сборки составляет порядка 25%, или использование памяти примерно на 50% больше? Раньше меня сильно беспокоили другие проблемы с созданием экземпляров шаблонов, поэтому я беспокоюсь.
Мой основной интерес — создание кортежей, а затем их итерация с помощью std::get, вероятно, с использованием кода в следующих строках:
// for_each_tuple: из https://www.cppstories.com/2022/tuple-iteration-apply/ шаблон void for_each_tuple(TupleT&& tp, Fn&& fn) { std::apply([&fn](T&&... args) { (fn(std::forward(args)), ...); }, std::forward(tp)); } std::get явно нерекурсивный, следовательно, O(1) для компилятора, для libstdc++, но код для msvc и gcc не так ясен; Я думаю, что msvc рекурсивен, O (n), но я не могу понять код gcc. Они оба рекурсивные?
msvc имеет std::get
шаблон constexpr tuple_element_t& get(tuple& _Tuple) noException { используя _Ttype = имя типа tuple_element::_Ttype; return static_cast(_Tuple)._Myfirst._Val; } то (в утилите)
шаблон struct tuple_element : tuple_element {}; // рекурсивное определение tuple_element gcc libstdc++
template constexpr __tuple_element_t& get(tuple& __t) noException { return std::__get_helper(__t); } template constexpr _Head& __get_helper(_Tuple_impl& __t) noException { return _Tuple_impl::_M_head(__t); } clang libc++
шаблон имя типа tuple_element::type& get(tuple& __t) _NOEXCEPT { typedef typename tuple_element::type type; return static_cast(__t.__base_).get(); } Две стандартные библиотеки, которые я использую, msvc и gcc, используют рекурсивный подход.
мсвк
шаблон class tuple : Private tuple { // определение рекурсивного кортежа публика: используя _This_type = _This; используя _Mybase = tuple; gcc
/** * Рекурсивная реализация кортежа. Здесь мы храним элемент @c Head. * и получить из @c Tuple_impl, содержащий остальные элементы * (который содержит хвост @c). */ шаблон struct _Tuple_impl : public _Tuple_impl, частная _Head_base { template struct _Tuple_impl; см. лязг
шаблон кортеж класса { typedef __tuple_impl _BaseT; _BaseT __base_; Подводя итог, если я правильно прочитал код стандартной библиотеки, кажется, что по крайней мере один из msvc и gcc std::get является рекурсивным, следовательно, O(n) для компилятора и O (n^2) для кода for_each, который я взял из историй cpp. Можно ли сократить итерацию до O(n) для компилятора? И даже в этом случае требуются ли компилятору значительные затраты времени и пространства из-за рекурсивного характера построения кортежей в msvc и gcc?
(Я использую msvc 2022 17.6, gcc 13.2, clang 16 и удалил некоторые шаблоны из выдержек из стандартных библиотек, чтобы облегчить чтение.)
Некоторые источники, например, «Почему нехорошо использовать рекурсивное наследование для реализаций std::tuple?», указывают на затраты времени компиляции при использовании рекурсивной реализации шаблона для кортежей. Я подумываю о написании системы, которая будет использовать сотни кортежей, каждый из которых может иметь размер от 1 до 100 (в среднем от 5 до 10, я думаю), и задаюсь вопросом, стоит ли мне заботиться о том, чтобы стандартные библиотеки, которые я буду использовать, имели рекурсивный подход. выполнение? Может ли кто-нибудь оценить это количественно? влияние на время сборки составляет порядка 25%, или использование памяти примерно на 50% больше? Раньше меня сильно беспокоили другие проблемы с созданием экземпляров шаблонов, поэтому я беспокоюсь.
Мой основной интерес — создание кортежей, а затем их итерация с помощью std::get, вероятно, с использованием кода в следующих строках:
// for_each_tuple: из https://www.cppstories.com/2022/tuple-iteration-apply/ шаблон void for_each_tuple(TupleT&& tp, Fn&& fn) { std::apply([&fn](T&&... args) { (fn(std::forward(args)), ...); }, std::forward(tp)); } std::get явно нерекурсивный, следовательно, O(1) для компилятора, для libstdc++, но код для msvc и gcc не так ясен; Я думаю, что msvc рекурсивен, O (n), но я не могу понять код gcc. Они оба рекурсивные?
msvc имеет std::get
шаблон constexpr tuple_element_t& get(tuple& _Tuple) noException { используя _Ttype = имя типа tuple_element::_Ttype; return static_cast(_Tuple)._Myfirst._Val; } то (в утилите)
шаблон struct tuple_element : tuple_element {}; // рекурсивное определение tuple_element gcc libstdc++
template constexpr __tuple_element_t& get(tuple& __t) noException { return std::__get_helper(__t); } template constexpr _Head& __get_helper(_Tuple_impl& __t) noException { return _Tuple_impl::_M_head(__t); } clang libc++
шаблон имя типа tuple_element::type& get(tuple& __t) _NOEXCEPT { typedef typename tuple_element::type type; return static_cast(__t.__base_).get(); } Две стандартные библиотеки, которые я использую, msvc и gcc, используют рекурсивный подход.
мсвк
шаблон class tuple : Private tuple { // определение рекурсивного кортежа публика: используя _This_type = _This; используя _Mybase = tuple; gcc
/** * Рекурсивная реализация кортежа. Здесь мы храним элемент @c Head. * и получить из @c Tuple_impl, содержащий остальные элементы * (который содержит хвост @c). */ шаблон struct _Tuple_impl : public _Tuple_impl, частная _Head_base { template struct _Tuple_impl; см. лязг
шаблон кортеж класса { typedef __tuple_impl _BaseT; _BaseT __base_; Подводя итог, если я правильно прочитал код стандартной библиотеки, кажется, что по крайней мере один из msvc и gcc std::get является рекурсивным, следовательно, O(n) для компилятора и O (n^2) для кода for_each, который я взял из историй cpp. Можно ли сократить итерацию до O(n) для компилятора? И даже в этом случае требуются ли компилятору значительные затраты времени и пространства из-за рекурсивного характера построения кортежей в msvc и gcc?
(Я использую msvc 2022 17.6, gcc 13.2, clang 16 и удалил некоторые шаблоны из выдержек из стандартных библиотек, чтобы облегчить чтение.)
Мобильная версия