Оптимизация макета класса путем безопасного минимизации отступов

У меня есть следующий тип, и моя цель — оптимизировать размер 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. Я знаю о битовых полях и намеренно не использую их из-за (доказанных) последствий для производительности.

вы уверены в этих sizeofs? не будет ли Дейте по крайней мере 52 года?

Neil Butterworth 02.11.2022 13:09

@NeilButterworth нет, это union, поэтому считается, что самый большой член имеет 36 байт, а затем выравнивается по самому большому типу данных, то есть 8 байт ==> 40.

mch 02.11.2022 13:11

@mch о боже, я пропустил это!

Neil Butterworth 02.11.2022 13:12

@Someprogrammerdude Это InlinedVector Abseil (под прикрытием). Прямо сейчас size/is_allocated используются как битовые поля (вроде). Это означает, что для чтения InlinedVector::size требуется ~4 инструкции. Если я смогу оптимизировать размер хранилища без использования битовых полей, то size чтение/запись станут заметно быстрее.

antonpp 02.11.2022 13:24
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это очень хорошо определено. От [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 02.11.2022 13:16

@antonpp Да. Вы должны проверить работоспособность, прикрепив static_assert(std::is_standard_layout_v<Inlined>); куда-нибудь.

Passer By 02.11.2022 13:22

Я думаю, что анонимная структура находится на территории расширения. Но если предположить, что он ведет себя так, как ожидалось ™, хороший ответ.

StoryTeller - Unslander Monica 02.11.2022 13:56

Другие вопросы по теме