Влияйте на выравнивание классов, чтобы плотно упаковывать переменные

Предположим, такие структуры: A находятся вне нашего контроля, но мы можем изменить структуру B:

struct A {
  int64_t x;  // 8 bytes
  int32_t y;  // 4 bytes
              // 4 padding bytes
};

struct B {
  A a;        // 16 bytes
  int32_t z;  // 4 bytes
              // 4 padding bytes (alignof(B) == alignof(A) == 8)
};
static_assert(sizeof(B) == 24);  // 8 useless bytes!

Есть ли способ поместить z внутрь A отступа (некоторые атрибуты)?

Я понимаю общую идею такого поведения, но что, если вы хотите добиться другого?

Alexander S 15.05.2024 22:34

В некоторых компиляторах есть packpragma, но будьте очень осторожны с его использованием. Эту структуру больше нельзя будет использовать в системах со строгими требованиями к выравниванию, и, вероятно, она будет работать медленнее в тех, которые этого не делают.

user4581301 15.05.2024 22:38

Вы можете создать A шаблон класса, чтобы он мог хранить любое количество дополнительной информации, например, в tuple.

Ted Lyngmo 15.05.2024 22:39

@TedLyngmo о A мы ничего не знаем - только то, что у него могут быть байты заполнения

Alexander S 15.05.2024 22:40

@AlexanderS Хорошо, тогда я бы оставил эти байты заполнения. Интересно, что вы знаете, что они у него есть. Компилятор предупреждает об этом?

Ted Lyngmo 15.05.2024 22:42

Я не получаю никаких предупреждений, но потеря 8 байт в этом случае заставляет меня плакать

Alexander S 15.05.2024 22:46

Тогда что делает A? Напишите свой A, где сможете максимально плотно упаковать всю информацию.

Ted Lyngmo 15.05.2024 22:49

Это скорее расследование (опять :)). B хранит A и какой-то примитивный тип, который мал по сравнению с A, и мне бы хотелось избежать сильного расширения B (что, если alignof(A) == 16?)

Alexander S 15.05.2024 22:53

@AlexanderS Ну, ради любопытства, у пользователя 4581301 есть ответ - у большинства компиляторов есть __attribute__, #pragma или любой другой специфичный для компилятора способ запретить отступы. Очевидно, что он непереносим, ​​поскольку каждый компилятор имеет свой собственный синтаксис для его написания, и, как уже упоминалось, он может нарушить требования к псевдонимам или, по крайней мере, замедлить доступ к структуре.

Yksisarvinen 15.05.2024 23:08

Используете ли вы классы стандартной библиотеки? Если да, то в них вы также можете найти 2- и 4-байтовые дополнения. Шаблоны классов, такие как std::vector, могут быть заполнены внутренними отступами, и наличие большого количества vector в приложении не является чем-то необычным.

Ted Lyngmo 15.05.2024 23:08

@Yksisarvinen Согласен, наверное, оно того не стоит, если мы хотим сохранить хорошую производительность.

Alexander S 15.05.2024 23:11

@TedLyngmo всегда) Ух ты, я думал, что контейнеры STL очень хорошо спроектированы (без отступов и т. д.).

Alexander S 15.05.2024 23:12

Взгляните на себя. Те, на которые я смотрел, хорошо спроектированы, а у некоторых есть отступы.

Ted Lyngmo 15.05.2024 23:14

@AlexanderS Я бы не стал рассматривать «отсутствие отступов» как характеристику хорошего дизайна. Если у вас есть система, в которой несколько байтов заполнения являются проблемой, вы все равно не будете использовать стандартные контейнеры (возможно, с баром std::array). Стандартные контейнеры в основном созданы для скорости и удобства использования. Единственный контейнер, рассчитанный на размер, — std::forward_list, и я никогда не видел, чтобы кто-нибудь им пользовался. Серьезно, я думаю, что ты фокусируешься не на том. Компьютеры имеют гигабайты оперативной памяти, встроенные системы — мегабайты. «Потратить» часть этого на скорость всегда лучше, если только вы действительно не оказались в ограниченной системе.

Yksisarvinen 15.05.2024 23:22

Они хорошо спроектированы, но если содержимое нуждается в дополнении, оно должно быть дополнено. А поскольку такое заполнение обычно приводит к более высокой скорости, скажем так, обмен оперативной памяти на скорость — это проверенная временем традиция программирования.

user4581301 15.05.2024 23:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
15
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Повторное использование заполнения определяется ABI, а не стандартом или компилятором. Например, в Linux это будет работать:

struct A {
  int64_t x;  // 8 bytes
  int32_t y;  // 4 bytes
              // 4 padding bytes
  A() = default;   // <-- CHANGE HERE
};

struct B {
  [[no_unique_address]] A a; // <-- CHANGE HERE
  int32_t z;  // 4 bytes in the padding of A
};

static_assert(sizeof(B) == 16);

божий болт

A необходимы определенные свойства, соответствующие его типу, чтобы иметь право на повторное использование хвостовой части. Для этого ABI он не должен быть POD, как в C++03. Достаточно добавить объявленный пользователем конструктор.

Затем в B нам нужно заставить использовать отступы хвоста. Например, если A является базовым классом, он будет повторно использовать свое хвостовое дополнение. Альтернативно, использование [[no_unique_address]] дает тот же результат.

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