Есть ли какая-либо гарантия выравнивания возвращаемого адреса с помощью новой операции C ++?
-
21-08-2019 - |
Вопрос
Большинство опытных программистов знают, что выравнивание данных важно для производительности программы.Я видел, как какой-то программист написал программу, которая выделяет больший размер буфера, чем им нужно, и использует выровненный указатель в качестве начала.Мне интересно, должен ли я сделать это в своей программе, я понятия не имею, есть ли какая-либо гарантия выравнивания адреса, возвращаемого новой операцией C ++.Поэтому я написал небольшую программу для тестирования
for(size_t i = 0; i < 100; ++i) {
char *p = new char[123];
if(reinterpret_cast<size_t>(p) % 4) {
cout << "*";
system("pause");
}
cout << reinterpret_cast<void *>(p) << endl;
}
for(size_t i = 0; i < 100; ++i) {
short *p = new short[123];
if(reinterpret_cast<size_t>(p) % 4) {
cout << "*";
system("pause");
}
cout << reinterpret_cast<void *>(p) << endl;
}
for(size_t i = 0; i < 100; ++i) {
float *p = new float[123];
if(reinterpret_cast<size_t>(p) % 4) {
cout << "*";
system("pause");
}
cout << reinterpret_cast<void *>(p) << endl;
}
system("pause");
Компилятор, который я использую, - Visual C ++ Express 2008.Похоже, что все адреса, возвращенные новой операцией, выровнены.Но я не уверен.Итак, мой вопрос заключается в следующем:есть ли какие-либо гарантии?Если у них действительно есть гарантия, мне не нужно соглашаться, если нет, я должен.
Решение
Выравнивание имеет следующую гарантию, предусмотренную стандартом (3.7.3.1/2)::
Возвращаемый указатель должен быть соответствующим образом выровнен, чтобы его можно было преобразовать в указатель любого полного типа объекта и затем использовать для доступа к объекту или массиву в выделенном хранилище (до хранилище явно освобождается вызовом соответствующей функции освобождения).
Редактировать:Благодаря время суток для выделения ошибка в gcc / glibc, где гарантия не действует.
ПРАВКА 2:Комментарий Бена освещает интересный случай edge.Требования к процедурам распределения относятся только к тем, которые предусмотрены стандартом.Если приложение имеет свою собственную версию, то такой гарантии на результат нет.
Другие советы
Это поздний ответ, но просто для того, чтобы прояснить ситуацию в Linux - на 64-разрядных системах память всегда выровнена по 16 байтам:
http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html
Адрес блока, возвращаемого malloc или realloc в системе GNU, всегда является кратным восьми (или шестнадцати в 64-разрядных системах).
В new
вызовы оператора malloc
внутренне
(см ./gcc/libstdc++-v3/libsupc++/new_op.cc
)
итак, это относится к new
также.
Реализация malloc
который является частью glibc
в основном определяет
MALLOC_ALIGNMENT
быть 2*sizeof(size_t)
и size_t
является 32-битным = 4 байта и 64-битным = 8 байт
в системах x86-32 и x86-64 соответственно.
$ cat ./glibc-2.14/malloc/malloc.c:
...
#ifndef INTERNAL_SIZE_T
#define INTERNAL_SIZE_T size_t
#endif
...
#define SIZE_SZ (sizeof(INTERNAL_SIZE_T))
...
#ifndef MALLOC_ALIGNMENT
#define MALLOC_ALIGNMENT (2 * SIZE_SZ)
#endif
Кстати , тот Документация MS упоминает что-то о адресах возврата malloc / new, которые выровнены по 16 байтам, но из экспериментов это не так.Так случилось, что мне понадобилось выравнивание на 16 байт для проекта (чтобы ускорить копирование памяти с помощью расширенного набора команд), в конце концов я прибегнул к написанию собственного распределителя...
Оператор платформы new /new[] вернет указатели с достаточным выравниванием, чтобы он мог хорошо работать с базовыми типами данных (double, float и т.д.).По крайней мере, любой разумный компилятор C ++ + среда выполнения должны это делать.
Если у вас есть особые требования к выравниванию, например, для SSE, то, вероятно, хорошей идеей будет использовать специальные функции aligned_malloc или использовать свои собственные.
Я работал над системой, где они использовали выравнивание, чтобы освободить лишний бит для собственного использования!
Они использовали нечетный бит для реализации системы виртуальной памяти.
Когда у указателя был установлен нечетный бит, они использовали это, чтобы показать, что он указывает (минус нечетный бит) на информацию для получения данных из базы данных, а не на сами данные.
Я думал, что это особенно неприятный фрагмент кодирования, который был слишком умен для своего же блага!!
Тони
C ++17 изменяет требования к new
распределитель, такой, что требуется возвращать указатель, выравнивание которого равно макро __STDCPP_DEFAULT_NEW_ALIGNMENT__
(который определяется реализацией, а не включением заголовка).
Это важно, потому что этот размер может быть более крупный чем alignof(std::max_align_t)
.Например, в Visual C ++ максимальное обычное выравнивание составляет 8 байт, но по умолчанию new
всегда возвращает 16-байтовую выровненную память.
Также обратите внимание, что если вы переопределите значение по умолчанию new
с вашим собственным распределителем вы требуемый соблюдать требования __STDCPP_DEFAULT_NEW_ALIGNMENT__
также.