Есть ли какая-либо гарантия выравнивания возвращаемого адреса с помощью новой операции C ++?

StackOverflow https://stackoverflow.com/questions/506518

Вопрос

Большинство опытных программистов знают, что выравнивание данных важно для производительности программы.Я видел, как какой-то программист написал программу, которая выделяет больший размер буфера, чем им нужно, и использует выровненный указатель в качестве начала.Мне интересно, должен ли я сделать это в своей программе, я понятия не имею, есть ли какая-либо гарантия выравнивания адреса, возвращаемого новой операцией 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__ также.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top