Предположим, что вам нужно реализовать класс static_vector<T, N>
, который является контейнером с фиксированной емкостью, который полностью живет в стеке и никогда не выделяет, и предоставляет интерфейс std::vector
. (Boost обеспечивает boost::static_vector
.)
Учитывая, что для максимальных N
экземпляров T
у нас должно быть неинициализированное хранилище, существует множество вариантов, которые можно сделать при разработке внутреннего расположения данных:
-
Одночастный
union
:union U { T _x; }; std::array<U, N> _data;
-
Одиночный
std::aligned_storage_t
:std::aligned_storage_t<sizeof(T) * N, alignof(T)> _data;
-
Массив
std::aligned_storage_t
:using storage = std::aligned_storage_t<sizeof(T), alignof(T)>; std::array<storage, N> _data;
Независимо от выбора, для создания членов потребуется использование "размещение new
" и доступ к ним потребует чего-то по строкам reinterpret_cast
.
Теперь предположим, что у нас есть две очень минимальные реализации static_vector<T, N>
:
-
with_union
: реализован с использованием подхода с одним членомunion
; -
with_storage
: реализован с использованием подхода "singlestd::aligned_storage_t
".
Позвольте выполнить следующий тест, используя как g++
, так и clang++
с -O3
. Я использовал quick-bench.com для этой задачи:
void escape(void* p) { asm volatile("" : : "g"(p) : "memory"); }
void clobber() { asm volatile("" : : : "memory"); }
template <typename Vector>
void test()
{
for(std::size_t j = 0; j < 10; ++j)
{
clobber();
Vector v;
for(int i = 0; i < 123456; ++i) v.emplace_back(i);
escape(&v);
}
}
(escape
и clobber
взяты из Chandler Carruth CppCon 2015 talk: "Настройка С++: тесты, и процессоры, и компиляторы! Oh My!" )
- Результаты для
g++ 7.2
(жить здесь):
- Результаты для
clang++ 5.0
(live here):
Как вы можете видеть из результатов, g++
, похоже, может агрессивно оптимизировать (векторизация) реализацию, использующую подход "single std::aligned_storage_t
", но не реализацию с помощью union
.
clang++
не может агрессивно оптимизировать любой из них.
Мои вопросы:
-
Есть ли что-нибудь в стандарте, которое предотвращает агрессивную оптимизацию реализации с помощью
union
? (Т.е. стандарт предоставляет больше свободы компилятору при использованииstd::aligned_storage_t
- если так почему?) -
Является ли это исключительно вопросом "качества реализации"?