Является ли выравнивание памяти в C ++ правильным или неэффективным?

Я протестировал этот код, просто пытаясь выяснить, сколько памяти c ++ фактически зарезервировано для нового оператора.

#include<iostream>
using namespace std;
int main() {

cout << "alignment of " << alignof(int) << endl;
int *intP1 = new int;
*intP1 = 100;
cout << "address of intP1 " << intP1 << endl;
int *intP2 = new int;
*intP2 = 999;
cout << "address of intP2 " << intP2 << endl;
int *intP3 = new int;
cout << "address of intP3 " << intP3 << endl;
*intP3 = 333;

cout << endl;
cout << (reinterpret_cast<char *>(intP3)-reinterpret_cast<char *>(intP2)) << endl;
cout << intP3-intP2 << endl;
cout << endl;

cout << *(intP1) << endl;
cout << *(intP1+4) << endl;
cout << *(intP1+8) << endl;
cout << *(intP1+16) << endl;
delete intP1;
delete intP2;
delete intP3;
return 0;
}

После того, как мы скомпилировали код с флагом -std = c ++ 11 и запустили его, вот что я получил с машины x86_64.

    alignment of int4
address of intP1 = 0xa59010
address of intP2 = 0xa59030
address of intP3 = 0xa59050

the distance of intP3 and intP2 = 32

intP1 value = 100
is this a padding value = 0
intP2 value = 999
intP3 value = 333

Кажется, что при использовании new для выделения 4-байтовой памяти для целого числа он фактически резервирует 32-байтовый блок, который является общим пространством для 8 целых чисел. Согласно объяснению выравнивания c ++, для 64-битной машины память выровнена на 16 байтов, почему расстояние здесь составляет 32 байта?

Может ли кто-нибудь помочь мне разобраться с этим? Заранее спасибо.

6

Решение

Это не имеет ничего общего с выравниванием — это дополнительные издержки работы распределителя внутренней памяти. Как правило, каждый блок памяти имеет дополнительную скрытую информацию в передней и / или задней части, которая используется для поддержания структуры кучи. Точные издержки будут зависеть от платформы к платформе и от реализации к реализации.

Например, Даг Ли malloc имеет дополнительные издержки 4-8 байтов на выделение (32-разрядные указатели) или 8-16 байтов (64-разрядные указатели) и минимальный размер выделения 16 байтов (32-разрядный) или 32 байта (64-разрядный). Это означает, что даже для однобайтовых выделений распределителю памяти требуется всего 16 байтов для отслеживания накладных расходов.

5

Другие решения

32-байтовая разница не просто для выравнивания. Действительно, обратите внимание, что адрес 0xa59010 не 32-выравнивается, это только 16-выравнивается. Таким образом, выравнивание ваших адресов не будет хуже, если они будут на расстоянии всего 16 байтов, а не 32.

Скорее разница в 32 байта — это издержки / неэффективность распределителя памяти. Я подозреваю, что распределитель:

  • услужливо дает вам 16 выровненных адресов. Это то, что вам нужно для 128-битных типов SSE, так что это полезно для вас, но я не знаю, является ли это основной причиной того, что распределитель является 16-выравнивающим, или это просто удобно для распределителя.
  • требует некоторого пространства «до» выделения для бухгалтерской информации, которое может составлять 16 байтов (2 указателя или указатель и размер), но даже если это не округляется до 16 байтов.
  • для ваших фактических данных требуется всего 4 байта, но из-за 16-байтового учета и 16-байтового выравнивания минимальное расстояние между выделениями составляет 32 байта. Таким образом, при выделении 4 байтов выделяется 12 байт «свободного пространства» / «внутренней фрагментации» / «отходов».

Но это всего лишь предположение, я не изучал тот распределитель, который вы используете.

2

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

1

У вас есть и указатель и значение памяти. Это выделяет 2 блока памяти, которые вы уже указали, по 16 байт. 2×16 = 32.

Я верю, что это даст вам результат, который вы искали.

  cout << "alignment of " << alignof(int) << endl;
int intP1 = 100;
cout << "address of intP1 " << &intP1 << endl;
int intP2 = 999;
cout << "address of intP2 " << &intP2 << endl;
int intP3 = 333;
cout << "address of intP3 " << &intP3 << endl;
0

int4 означает, что он занимает 4 байта, а не 4 бита. Все, что показывает вам компилятор, действительно верно! Вот некоторая документация по примитивам и что int примитивные средства.

Вот учебник о том, как определить 4-битное целое число.

Позвольте мне упомянуть, что alignof зависит от архитектуры. В некоторых архитектурах int означает 16 бит int или 2 байта вместо 4.

0

Абсолютно ничего не требует, чтобы операционная система выделяла три указателя intP1, intP2 а также intP3 рядом друг с другом. Ваш код может обнаруживать накладные расходы в распределениях (и, конечно, это разумное предположение, что они есть), но этого недостаточно, чтобы доказать, что интервал — это все накладные расходы распределителя.

0

Стандарт C ++ не дает никаких гарантий того, сколько накладных расходов имеет каждое выделение кучи. Независимо от выравнивания распределитель обычно добавляет дополнительные издержки.
Распределители очень часто выполняют небольшие выделения из заранее заданных блоков. Здесь кажется, что наименьшая область составляет 32 байта на распределение, что не является необычным. В дикой природе вы редко найдете распределители с сегментами меньше 16 байт.

Если бы вы выделяли более 1 int, скажем, int [2], вы, вероятно, заметили бы, что занимаемый объем памяти идентичен: 32 байта.

Также обратите внимание, что нет никаких гарантий от стандарта C ++ или распределителей, что 2 выделения одинакового размера будут смежными. Это может соблюдаться большую часть времени, но на него нельзя полагаться.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector