У меня есть следующий тип, и моя цель — оптимизировать размер Storage
:
struct Inlined { int data[9]; }; // sizeof => 36
struct Allocated { int* data; size_t capacity }; // sizeof => 16
union Data { Inlined inlined; Allocated allocated; }; // sizeof => 40
struct Storage { // sizeof => 56
size_t size;
bool is_allocated;
Data data;
};
Я понимаю, что размеры и макет могут различаться в зависимости от компилятора и платформы. Моя цель - получить более компактную компоновку хотя бы на некоторых компиляторах/платформах.
Как вы можете видеть в моем примере, Data
имеет 4 байта заполнения, потому что Inlined
и Allocated
имеют разное выравнивание, а Data
выровнено по 8 байтам. Я пытаюсь найти способ втиснуть логическое значение is_allocated
в это дополнение.
До сих пор я придумал следующую структуру, и я был бы очень признателен, если бы кто-то мог подтвердить или опровергнуть «безопасность» / «законность» такого типа:
struct CompactStorage { // sizeof => 48
size_t size;
union {
struct {
bool is_allocated;
Inlined inlined;
};
struct {
bool copy_of_is_allocated;
Allocated allocated;
};
};
};
Чтобы уточнить, под безопасностью я подразумеваю, что я ожидаю, что CompactStorage::is_allocated
и CompactStorage::copy_of_is_allocated
будут совмещать одну и ту же память. И я ожидаю, что если я пишу в CompactStorage::is_allocated
, то он не перезаписывает байты с псевдонимом CompactStorage::allocated
(и наоборот). Если это не так, то я считаю CompactStorage
небезопасным, поскольку его нельзя использовать в качестве замены Storage
.
P.S. Я знаю о битовых полях и намеренно не использую их из-за (доказанных) последствий для производительности.
@NeilButterworth нет, это union
, поэтому считается, что самый большой член имеет 36
байт, а затем выравнивается по самому большому типу данных, то есть 8
байт ==> 40
.
@mch о боже, я пропустил это!
@Someprogrammerdude Это InlinedVector Abseil (под прикрытием). Прямо сейчас size/is_allocated
используются как битовые поля (вроде). Это означает, что для чтения InlinedVector::size
требуется ~4 инструкции. Если я смогу оптимизировать размер хранилища без использования битовых полей, то size
чтение/запись станут заметно быстрее.
Это очень хорошо определено. От [class.mem]
В объединении стандартного макета с активным членом типа структуры T1 разрешается считывать нестатический элемент данных m другого члена объединения типа структуры T2 при условии, что m является частью общей начальной последовательности T1 и T2; поведение такое, как если бы был назначен соответствующий член T1.
Где общая начальная последовательность
Общая начальная последовательность двух типов структур стандартного макета — это самая длинная последовательность нестатических элементов данных и битовых полей в порядке объявления, начиная с первого такого объекта в каждой из структур, так что соответствующие объекты имеют типы, совместимые с макетом. [...]
Это означает, что вы можете использовать is_allocated
и copy_of_is_allocated
взаимозаменяемо, учитывая, что структуры являются типами стандартного макета.
Замечательно! Спасибо! Итак, я полагаю, что если Inlined
выглядит так template <typename T> struct Inlined { alignas(T) char inlined_data[sizeof(T[N])]; };
Это все еще считается стандартным типом макета, верно?
@antonpp Да. Вы должны проверить работоспособность, прикрепив static_assert(std::is_standard_layout_v<Inlined>);
куда-нибудь.
Я думаю, что анонимная структура находится на территории расширения. Но если предположить, что он ведет себя так, как ожидалось ™, хороший ответ.
вы уверены в этих sizeofs? не будет ли Дейте по крайней мере 52 года?