[Не дубликат заполнения структуры и упаковки. Этот вопрос о том, как и когда происходит заполнение. Этот рассказ о том, как с этим бороться.]
Я только что понял, сколько памяти теряется в результате выравнивания в C++. Рассмотрим следующий простой пример:
struct X
{
int a;
double b;
int c;
};
int main()
{
cout << "sizeof(int) = " << sizeof(int) << '\n';
cout << "sizeof(double) = " << sizeof(double) << '\n';
cout << "2 * sizeof(int) + sizeof(double) = " << 2 * sizeof(int) + sizeof(double) << '\n';
cout << "but sizeof(X) = " << sizeof(X) << '\n';
}
При использовании g++ программа выдает следующий вывод:
sizeof(int) = 4
sizeof(double) = 8
2 * sizeof(int) + sizeof(double) = 16
but sizeof(X) = 24
Это 50% памяти! В 3-гигабайтном массиве 134'217'728 X
1 гигабайт будет чистое заполнение.
К счастью, решение проблемы очень простое - мы просто должны поменять местами double b
и int c
:
struct X
{
int a;
int c;
double b;
};
Теперь результат гораздо более удовлетворительный:
sizeof(int) = 4
sizeof(double) = 8
2 * sizeof(int) + sizeof(double) = 16
but sizeof(X) = 16
Однако есть проблема: это не является кросс-совместимым. Да, в g++ значение int
составляет 4 байта, а double
- 8 байтов, но это не всегда верно (их выравнивание не обязательно должно быть одинаковым), поэтому в другой среде это "исправление" могло не только быть бесполезным, но это может потенциально ухудшить ситуацию, увеличив необходимое количество отступов.
Существует ли надежный кроссплатформенный способ решения этой проблемы (минимизировать количество необходимого заполнения, не страдая от снижения производительности, вызванного смещением)? Почему компилятор не выполняет такую оптимизацию (меняйте местами члены структуры/класса, чтобы уменьшить заполнение)?
осветление
Из-за недопонимания и путаницы я хотел бы подчеркнуть, что я не хочу "упаковывать" свою struct
. То есть я не хочу, чтобы его члены были выровнены и, следовательно, доступ к ним был медленнее. Вместо этого я по-прежнему хочу, чтобы все члены были выровнены самостоятельно, но таким образом, чтобы при заполнении использовалось меньше всего памяти. Эту проблему можно решить, используя, например, ручную перестановку, как описано здесь и в книге "Потерянное искусство упаковки " Эрика Рэймонда. Я ищу автоматизированный и максимально кроссплатформенный способ сделать это, подобный тому, что описано в предложении P1112 для готовящегося стандарта C++ 20.