Как выделить выровненную память только с помощью стандартной библиотеки?
Я только что прошел тест в рамках собеседования, и 9X_memory-allocation один вопрос поставил меня в тупик, даже 9X_memory-usage используя Google для справки. Я бы хотел 9X_memory-management посмотреть, что команда StackOverflow может 9X_memory-deallocation с этим сделать:
Функция
memset_16aligned
требует передачи 9X_c 16-байтового выровненного указателя, в противном 9X_memory-usage случае произойдет сбой.а) Как бы вы выделили 9X_memory-management 1024 байта памяти и выровняли ее по границе 9X_memory-allocation 16 байтов?
б) Освободите память после выполнения 9X_memory-managementmemset_16aligned
.
{ void *mem; void *ptr; // answer a) here memset_16aligned(ptr, 0, 1024); // answer b) here }
- @ StevenA.Lowe Существуют допустимые варианты использования, требующие 16-байтовой выровненной памяти, когда вы хотите, чтобы используемый код мог работать _ действительно быстро_ без дополнительных проверок. Графический фреймворк Apple Me ...
Ответ #1
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Исходный ответ
{ void *mem = malloc(1024+16); void *ptr = ((char *)mem+16) & ~ 0x0F; memset_16aligned(ptr, 0, 1024); free(mem); }
Фиксированный ответ
{ void *mem = malloc(1024+15); void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F; memset_16aligned(ptr, 0, 1024); free(mem); }
Пояснения по запросу
Первым делом на всякий случай выделите достаточно 9X_memory-usage свободного места. Поскольку память должна 9X_c быть выровнена по 16 байт (это означает, что 9X_memory-allocation адрес ведущего байта должен быть кратен 9X_memory-management 16), добавление 16 дополнительных байтов 9X_memory-allocation гарантирует, что у нас достаточно места. Где-то 9X_memory-allocation в первых 16 байтах находится 16-байтовый 9X_c выровненный указатель. (Обратите внимание, что 9X_memory-allocation malloc()
должен возвращать указатель, который достаточно 9X_c хорошо выровнен для любой цели. Однако значение 9X_c слова «любое» в первую очередь относится 9X_memory-allocation к таким вещам, как базовые типы - long
, double
, long double
, long long
и 9X_memory-deallocation указатели на объекты и указатели на функции. Когда 9X_c вы делаете более специализированные вещи, например, играете 9X_memory-usage с графическими системами, им может потребоваться 9X_memory-deallocation более строгое выравнивание, чем остальная 9X_memory-deallocation система - отсюда вопросы и ответы вот так.)
Следующим 9X_memory-allocation шагом является преобразование указателя 9X_memory-management void в указатель char; Несмотря на GCC, вы 9X_memory-allocation не должны выполнять арифметические операции 9X_memory-allocation с указателями на недействительные указатели 9X_memory-usage (и GCC имеет параметры предупреждения, чтобы 9X_memory-deallocation сообщить вам, когда вы злоупотребляете им). Затем 9X_memory-allocation добавьте 16 к начальному указателю. Предположим, malloc()
вернул 9X_memory-management вам невероятно плохо выровненный указатель: 0x800001. Добавление 9X_memory-allocation 16 дает 0x800011. Теперь я хочу округлить 9X_memory-management до 16-байтовой границы, поэтому я хочу сбросить 9X_memory-management последние 4 бита на 0. 0x0F имеет последние 9X_memory-usage 4 бита, равные единице; поэтому все биты 9X_memory-allocation ~0x0F
равны единице, кроме последних четырех. И 9X_c это с 0x800011 дает 0x800010. Вы можете 9X_c перебирать другие смещения и видеть, что 9X_memory-allocation та же арифметика работает.
Последний шаг, free()
, прост: вы 9X_memory-management всегда и только возвращаетесь в free()
значение, которое 9X_memory-deallocation вам вернуло одно из malloc()
, calloc()
или realloc()
- все остальное 9X_memory-allocation - катастрофа. . Вы правильно указали mem
для 9X_memory-allocation хранения этого значения - спасибо. Бесплатная 9X_c версия освобождает его.
Наконец, если вы 9X_memory-usage знаете о внутреннем устройстве пакета malloc
вашей 9X_memory-deallocation системы, вы можете догадаться, что он вполне 9X_c может возвращать данные с выравниванием 9X_memory-usage по 16 байт (или с выравниванием по 8 байт). Если 9X_memory-deallocation бы он был выровнен по 16 байт, вам не нужно 9X_memory-management было бы возиться со значениями. Однако это 9X_memory-deallocation хитроумный и непереносимый вариант - другие 9X_memory-deallocation пакеты malloc
имеют разные минимальные выравнивания, и, следовательно, предположение 9X_c об одном, когда он делает что-то другое, приведет 9X_memory-allocation к дампам ядра. В широких пределах это решение 9X_memory-deallocation портативно.
Кто-то еще упомянул posix_memalign()
как еще 9X_memory-allocation один способ получить выровненную память; это 9X_memory-deallocation доступно не везде, но часто может быть реализовано 9X_memory-deallocation на его основе. Обратите внимание, что было 9X_memory-management удобно, что выравнивание было степенью двойки; другие 9X_memory-usage выравнивания сложнее.
Еще один комментарий 9X_memory-usage - этот код не проверяет успешность выделения.
Поправка
Windows Programmer указал, что 9X_c вы не можете выполнять операции с битовой 9X_c маской для указателей, и, действительно, GCC 9X_c (протестированные 3.4.6 и 4.3.1) действительно 9X_memory-usage жалуются на это. Итак, следует измененная 9X_memory-deallocation версия основного кода, преобразованная в 9X_memory-usage основную программу. Я также позволил себе 9X_memory-management добавить всего 15 вместо 16, как уже указывалось. Я 9X_memory-usage использую uintptr_t
, поскольку C99 существует достаточно 9X_memory-usage давно, чтобы быть доступным на большинстве 9X_c платформ. Если бы не использование PRIXPTR
в операторах 9X_c printf()
, было бы достаточно #include
вместо использования 9X_memory-allocation #include
. [Этот код включает исправление, указанное C.R., которое повторяет точку зрения, впервые высказанную Bill K несколько лет назад, которую мне удавалось упускать из виду до сих пор.]
#include #include #include #include #include static void memset_16aligned(void *space, char byte, size_t nbytes) { assert((nbytes & 0x0F) == 0); assert(((uintptr_t)space & 0x0F) == 0); memset(space, byte, nbytes); // Not a custom implementation of memset() } int main(void) { void *mem = malloc(1024+15); void *ptr = (void *)(((uintptr_t)mem+15) & ~ (uintptr_t)0x0F); printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr); memset_16aligned(ptr, 0, 1024); free(mem); return(0); }
А вот несколько более обобщенная версия, которая 9X_memory-allocation будет работать для размеров, равных степени 9X_c двойки:
#include #include #include #include #include static void memset_16aligned(void *space, char byte, size_t nbytes) { assert((nbytes & 0x0F) == 0); assert(((uintptr_t)space & 0x0F) == 0); memset(space, byte, nbytes); // Not a custom implementation of memset() } static void test_mask(size_t align) { uintptr_t mask = ~(uintptr_t)(align - 1); void *mem = malloc(1024+align-1); void *ptr = (void *)(((uintptr_t)mem+align-1) & mask); assert((align & (align - 1)) == 0); printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr); memset_16aligned(ptr, 0, 1024); free(mem); } int main(void) { test_mask(16); test_mask(32); test_mask(64); test_mask(128); return(0); }
Чтобы преобразовать test_mask()
в функцию распределения 9X_c общего назначения, единственное возвращаемое 9X_c значение распределителя должно закодировать 9X_c адрес выпуска, как указали несколько человек 9X_c в своих ответах.
Проблемы с интервьюерами
Uri прокомментировал: Возможно, у 9X_memory-deallocation меня сегодня утром проблема с пониманием 9X_c прочитанного, но если в вопросе собеседования 9X_memory-management конкретно сказано: «Как бы вы распределили 9X_memory-usage 1024 байта памяти?» и вы явно выделяете 9X_c больше, чем это. Разве это не будет автоматическим 9X_memory-deallocation провалом интервьюера?
Мой ответ не умещается 9X_memory-usage в 300-символьный комментарий ...
Думаю, это 9X_memory-usage зависит от обстоятельств. Я думаю, что большинство 9X_memory-usage людей (включая меня) восприняли этот вопрос 9X_memory-usage как означающий: «Как бы вы выделить пространство, в 9X_memory-management котором могут храниться 1024 байта данных, и 9X_memory-allocation где базовый адрес кратен 16 байтам». Если 9X_memory-allocation интервьюер действительно имел в виду, как 9X_memory-deallocation выделить 1024 байта (только) и выровнять 9X_memory-deallocation его по 16 байтов, то варианты более ограничены.
- Очевидно, что одна из возможностей - выделить 1024 байта, а затем дать этому адресу «обработку выравнивания»; Проблема с этим подходом заключается в том, что фактическое доступное пространство не определено должным образом (полезное пространство составляет от 1008 до 1024 байтов, но не было механизма, позволяющего указать, какой размер), что делает его менее полезным.
- Другая возможность состоит в том, что вы должны написать полный распределитель памяти и убедиться, что возвращаемый вами 1024-байтовый блок правильно выровнен. Если это так, вы, вероятно, в конечном итоге выполните операцию, довольно похожую на то, что было сделано в предложенном решении, но вы скрываете ее внутри распределителя.
Однако, если 9X_memory-usage интервьюер ожидал любого из этих ответов, я 9X_c бы ожидал, что он поймет, что это решение 9X_memory-usage отвечает на тесно связанный вопрос, а затем 9X_memory-usage перефразирует свой вопрос, чтобы направить 9X_c разговор в правильном направлении. (Кроме 9X_memory-usage того, если бы интервьюер стал действительно 9X_memory-management нервным, тогда я бы не захотел работать; если 9X_memory-management ответ на недостаточно точное требование 9X_memory-management будет уничтожен пламенем без исправлений, тогда 9X_memory-deallocation интервьюер не тот, для кого безопасно работать.)
Мир движется
Заголовок 9X_memory-deallocation вопроса недавно изменился. Меня озадачил 9X_memory-deallocation вопрос Решить выравнивание памяти в вопросе на C. Пересмотренный заголовок (Как выделить выровненную память только с помощью стандартной библиотеки?) требует 9X_memory-allocation немного измененного ответа - это приложение 9X_memory-management предоставляет его.
C11 (ISO / IEC 9899: 2011) добавлена 9X_memory-deallocation функция aligned_alloc()
:
7.22.3.1 Функция
aligned_alloc
Сводка
#include void *aligned_alloc(size_t alignment, size_t size);
Описание
Функцияaligned_alloc
выделяет место для 9X_memory-allocation объекта, выравнивание которого указывается 9X_memory-usagealignment
, размер которого задаетсяsize
, а значение 9X_c - неопределенный. Значениеalignment
должно быть 9X_memory-management допустимым выравниванием, поддерживаемым 9X_memory-management реализацией, а значениеsize
должно быть целым 9X_memory-deallocation кратнымalignment
.Возврат
Функцияaligned_alloc
возвращает либо нулевой 9X_memory-management указатель, либо указатель на выделенное 9X_memory-usage пространство.
И POSIX определяет posix_memalign()
:
#include int posix_memalign(void **memptr, size_t alignment, size_t size);
ОПИСАНИЕ
Функция 9X_memory-management
posix_memalign()
должна выделять байтыsize
, выровненные по 9X_memory-allocation границе, указанной вalignment
, и должна возвращать 9X_memory-deallocation указатель на выделенную память вmemptr
. Значение 9X_memory-deallocationalignment
должно быть степенью двойки, кратнойsizeof(void *)
.После 9X_memory-allocation успешного завершения значение, на которое 9X_memory-allocation указывает
memptr
, должно быть кратнымalignment
.Если размер 9X_memory-usage запрошенного пространства равен 0, поведение 9X_c определяется реализацией; значение, возвращаемое 9X_memory-management в
memptr
, должно быть либо нулевым указателем, либо 9X_memory-allocation уникальным указателем.Функция
free()
освобождает 9X_memory-usage память, которая ранее была выделенаposix_memalign()
.ВОЗВРАЩЕННАЯ 9X_memory-usage СТОИМОСТЬ
После успешного завершения
posix_memalign()
должен 9X_memory-deallocation вернуть ноль; в противном случае должен 9X_memory-management быть возвращен номер ошибки, указывающий 9X_memory-allocation на ошибку.
Любой из них или оба могут быть 9X_memory-allocation использованы для ответа на вопрос сейчас, но 9X_memory-usage при первоначальном ответе на вопрос можно 9X_memory-allocation было использовать только функцию POSIX.
За 9X_c кулисами новая функция выровненной памяти 9X_memory-usage выполняет во многом ту же работу, что и 9X_memory-allocation описанная в вопросе, за исключением того, что 9X_memory-deallocation они имеют возможность более легко принудительно 9X_memory-allocation выполнять выравнивание и отслеживать начало 9X_memory-deallocation выровненной памяти внутренне, чтобы код 9X_c не нужно иметь дело специально - он просто 9X_memory-allocation освобождает память, возвращаемую использованной 9X_memory-management функцией распределения.
- Просто из любопытства, `printf (" 0x% 08 "PRIXPTR", 0x% 08 "PRIXPTR" \ n ", mem, ptr);` неправильно, не так ли? Изменение его на `printf ("% x PRIXPTR,% x PRIXPT ...
Ответ #2
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Три немного разных ответа в зависимости 9X_memory-deallocation от того, как вы смотрите на вопрос:
1) Достаточно 9X_memory-management хорошо для точного ответа на заданный вопрос 9X_memory-deallocation решение Джонатана Леффлера, за исключением 9X_c того, что для округления до 16-выравниваемого 9X_memory-deallocation вам нужно всего 15 дополнительных байтов, а 9X_c не 16.
О:
/* allocate a buffer with room to add 0-15 bytes to ensure 16-alignment */ void *mem = malloc(1024+15); ASSERT(mem); // some kind of error-handling code /* round up to multiple of 16: add 15 and then round down by masking */ void *ptr = ((char*)mem+15) & ~ (size_t)0x0F;
Б:
free(mem);
2) Для более общей функции распределения 9X_memory-allocation памяти вызывающий не хочет отслеживать два 9X_c указателя (один для использования и один 9X_memory-allocation для освобождения). Таким образом, вы сохраняете 9X_c указатель на «настоящий» буфер под выровненным 9X_c буфером.
О:
void *mem = malloc(1024+15+sizeof(void*)); if (!mem) return mem; void *ptr = ((char*)mem+sizeof(void*)+15) & ~ (size_t)0x0F; ((void**)ptr)[-1] = mem; return ptr;
Б:
if (ptr) free(((void**)ptr)[-1]);
Обратите внимание, что в отличие 9X_memory-usage от (1), где в mem было добавлено только 9X_c 15 байтов, этот код может фактически уменьшить выравнивание, если 9X_memory-management ваша реализация гарантирует 32-байтовое 9X_memory-deallocation выравнивание из malloc (маловероятно, но 9X_memory-management в Теоретически реализация C может иметь 9X_memory-allocation 32-байтовый выровненный тип). Это не имеет 9X_memory-usage значения, если все, что вы делаете, это 9X_memory-allocation вызовите memset_16aligned, но если вы используете 9X_memory-allocation память для структуры, это может иметь значение.
Я 9X_memory-management не уверен, что для этого нужно хорошее исправление 9X_c (кроме предупреждения пользователя о том, что 9X_c возвращаемый буфер не обязательно подходит 9X_memory-deallocation для произвольных структур), поскольку нет 9X_memory-deallocation способа программно определить, какое выравнивание 9X_memory-management для конкретной реализации гарантия есть. Я 9X_memory-allocation предполагаю, что при запуске вы можете выделить 9X_memory-management два или более однобайтовых буфера и предположить, что 9X_c худшее выравнивание, которое вы видите, - это 9X_c гарантированное выравнивание. Если вы ошибаетесь, вы 9X_memory-usage тратите память. Любой, у кого есть идея 9X_memory-management получше, скажите, пожалуйста ...
[Добавлено: «Стандартный» трюк 9X_memory-allocation состоит в том, чтобы создать объединение 9X_memory-usage «типов, которые могут быть максимально выровненными» для 9X_memory-management определения необходимого выравнивания. Максимально 9X_memory-usage выровненные типы, вероятно, будут (в C99) 'long long
', 'long double
', 'void *
' или 9X_c 'void (*)(void)
'; если вы включите , вы, вероятно, можете 9X_memory-allocation использовать
intmax_t
вместо long long
(а на машинах Power 9X_c 6 (AIX) intmax_t
даст вам 128-битный целочисленный 9X_memory-usage тип). Требования к выравниванию для этого 9X_memory-deallocation объединения могут быть определены путем 9X_memory-usage встраивания его в структуру с одним символом, за 9X_c которым следует объединение:
struct alignment { char c; union { intmax_t imax; long double ldbl; void *vptr; void (*fptr)(void); } u; } align_data; size_t align = (char *)&align_data.u.imax - &align_data.c;
Затем вы должны 9X_c использовать большее из запрошенного выравнивания 9X_memory-usage (в примере - 16) и значение align
, вычисленное 9X_memory-usage выше.
В (64-битной) Solaris 10 кажется, что 9X_memory-management базовое выравнивание для результата malloc()
кратно 9X_memory-deallocation 32 байтам.
]
На практике выровненные распределители 9X_memory-usage часто принимают параметр для выравнивания, а 9X_memory-management не зашиты. Таким образом, пользователь передаст 9X_memory-usage размер структуры, которая ему небезразлична 9X_memory-allocation (или наименьшую степень двойки, большую 9X_memory-deallocation или равную ему), и все будет хорошо.
3) Используйте 9X_memory-usage то, что предоставляет ваша платформа: posix_memalign
для 9X_memory-allocation POSIX, _aligned_malloc
для Windows.
4) Если вы используете 9X_memory-usage C11, то самый чистый - переносимый и краткий 9X_memory-deallocation - вариант - использовать стандартную библиотечную 9X_memory-allocation функцию aligned_alloc
, которая была представлена в 9X_memory-deallocation этой версии спецификации языка.
- Я возражаю против использования ASSERT (mem); для проверки результатов распределения; `assert` предназначен для выявления ошибок программирования ...
Ответ #3
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Вы также можете попробовать posix_memalign()
(конечно, на 9X_memory-management платформах POSIX).
- И _aligned_malloc в Windows.<p><s ...
Ответ #4
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Вот альтернативный подход к части "округления". Не 9X_c самое блестяще закодированное решение, но 9X_c оно выполняет свою работу, и этот тип синтаксиса 9X_memory-deallocation немного легче запомнить (плюс будет работать 9X_memory-allocation для значений выравнивания, которые не являются 9X_memory-usage степенью двойки). Приведение uintptr_t
было необходимо, чтобы 9X_memory-usage успокоить компилятор; Арифметика с указателями 9X_memory-allocation не очень любит деление или умножение.
void *mem = malloc(1024 + 15); void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16; memset_16aligned(ptr, 0, 1024); free(mem);
- В общем, если у вас есть 'unsigned long long', у вас также есть uintptr_t, который явно определен как достаточно большой для хранения указателя данных (void ...
Ответ #5
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
К сожалению, в C99 довольно сложно гарантировать 9X_memory-allocation любое выравнивание таким образом, чтобы 9X_memory-usage его можно было переносить в любую реализацию 9X_memory-usage C, соответствующую C99. Почему? Поскольку 9X_c указатель не обязательно является «байтовым 9X_memory-deallocation адресом», это можно представить с помощью 9X_memory-management плоской модели памяти. Также не гарантируется 9X_memory-management представление uintptr_t, который в любом случае является 9X_memory-deallocation необязательным типом.
Возможно, мы знаем 9X_memory-usage о некоторых реализациях, которые используют 9X_memory-deallocation представление для void * (и по определению также 9X_memory-allocation char *), которое является простым байтовым адресом, но 9X_memory-deallocation в C99 оно непонятно для нас, программистов. Реализация 9X_c может представлять указатель в виде набора 9X_memory-usage {сегмента, смещения}, где смещение может иметь неизвестное выравнивание 9X_memory-deallocation «в действительности». Почему, указатель 9X_memory-allocation может даже быть некоторой формой значения 9X_memory-usage поиска в хеш-таблице или даже значением 9X_memory-usage поиска связанного списка. Он может кодировать 9X_memory-usage информацию о границах.
В недавнем черновике 9X_memory-management C1X для стандарта C мы видим ключевое слово 9X_memory-deallocation _Alignas. Это может немного помочь.
Единственная 9X_c гарантия, которую дает нам C99, заключается 9X_memory-allocation в том, что функции распределения памяти 9X_memory-usage будут возвращать указатель, подходящий для 9X_memory-usage присвоения указателю, указывающему на любой 9X_memory-deallocation тип объекта. Поскольку мы не можем указать 9X_memory-management выравнивание объектов, мы не можем реализовать 9X_memory-deallocation наши собственные функции распределения, отвечающие 9X_memory-deallocation за выравнивание, четко определенным переносимым 9X_memory-management способом.
Было бы неплохо ошибиться в этом 9X_c утверждении.
- В C11 есть `align_alloc()`. (C++ 11/14 / 1z до сих пор не имеет). _Alignas() и C++ alignas() ничего не делают для динамического распределения, только для автоматического и статическо ...
Ответ #6
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
При заполнении 16 и 15 байтов фактическое 9X_memory-allocation число, которое вам нужно добавить, чтобы 9X_memory-deallocation получить выравнивание N, равно max (0, NM), где M - естественное 9X_memory-deallocation выравнивание памяти. распределитель (и оба 9X_memory-allocation являются степенью двойки).
Поскольку минимальное 9X_memory-usage выравнивание памяти любого распределителя 9X_memory-allocation составляет 1 байт, 15 = max (0,16-1) - консервативный 9X_memory-usage ответ. Однако, если вы знаете, что ваш распределитель 9X_memory-allocation памяти будет выдавать вам 32-битные адреса, выровненные 9X_c по int (что довольно часто), вы могли бы 9X_memory-allocation использовать 12 как площадку.
Это не важно 9X_memory-usage для этого примера, но может быть важно для 9X_memory-deallocation встроенной системы с 12 КБ ОЗУ, где учитывается 9X_c каждое сохраненное целое число.
Лучший способ 9X_memory-management реализовать его, если вы на самом деле собираетесь 9X_c сохранить каждый возможный байт, - это использовать 9X_c макрос, чтобы вы могли скормить ему свое 9X_memory-management выравнивание памяти. Опять же, это, вероятно, полезно 9X_memory-management только для встроенных систем, где вам нужно 9X_memory-deallocation сохранять каждый байт.
В приведенном ниже 9X_memory-usage примере для большинства систем значение 9X_memory-management 1 вполне подходит для MEMORY_ALLOCATOR_NATIVE_ALIGNMENT
, однако для нашей 9X_c теоретической встроенной системы с 32-битными 9X_memory-management выровненными выделениями следующее могло 9X_memory-management бы сэкономить крошечный бит драгоценной 9X_memory-allocation памяти:
#define MEMORY_ALLOCATOR_NATIVE_ALIGNMENT 4 #define ALIGN_PAD2(N,M) (((N)>(M)) ? ((N)-(M)) : 0) #define ALIGN_PAD(N) ALIGN_PAD2((N), MEMORY_ALLOCATOR_NATIVE_ALIGNMENT)
Ответ #7
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Может быть, им понравилось бы знать memalign? И, как 9X_memory-allocation указывает Джонатан Леффлер, есть две более 9X_memory-deallocation новые функции, о которых следует знать.
Ой, Флорин 9X_memory-allocation меня опередил. Однако, если вы прочитаете 9X_c страницу руководства, на которую я ссылаюсь, вы, скорее 9X_memory-deallocation всего, поймете пример, представленный на 9X_memory-deallocation более раннем постере.
- Обратите внимание, что в текущей (февраль 2016 г.) версии [указанной страницы] (http://www.gnu.org/s/libc/manual/html_node/Aligned-Memory-Blocks.html) говорится: «Функция memalign obsolete и вместо него следует использов ...
Ответ #8
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Я удивлен, что никто не проголосовал за 9X_memory-deallocation Shao за answer, который, насколько я понимаю, невозможно 9X_memory-usage выполнить в стандартном C99, поскольку преобразование 9X_memory-usage указателя в интегральный тип формально является 9X_memory-management неопределенным поведением. (Помимо стандарта, разрешающего 9X_memory-management преобразование uintptr_t
<-> void*
, но стандарт, похоже, не 9X_memory-allocation позволяет выполнять какие-либо манипуляции 9X_memory-allocation со значением uintptr_t
, а затем преобразовывать его 9X_memory-usage обратно.)
Ответ #9
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Мы постоянно делаем такие вещи для Accelerate.framework, сильно 9X_memory-usage векторизованной библиотеки OS X / iOS, где 9X_memory-usage мы должны постоянно уделять внимание выравниванию. Есть 9X_memory-management довольно много вариантов, один или два из 9X_c которых я не упоминал выше.
Самый быстрый 9X_memory-allocation способ получить такой небольшой массив - просто 9X_memory-deallocation поместить его в стек. С GCC / clang:
void my_func( void ) { uint8_t array[1024] __attribute__ ((aligned(16))); ... }
Free() не 9X_memory-allocation требуется. Обычно это две инструкции: вычесть 9X_memory-allocation 1024 из указателя стека, затем И указатель 9X_memory-usage стека с -alignment. Предположительно, запрашивающей 9X_memory-usage стороне потребовались данные в куче, потому 9X_memory-management что срок ее жизни в массиве превысил стек, или 9X_memory-usage работает рекурсия, или пространство стека 9X_memory-management слишком дорого.
В OS X / iOS все вызовы malloc 9X_memory-allocation / calloc / и т. д. всегда выровнены по 16 9X_memory-allocation байт. Если вам, например, нужно выровнять 9X_c 32 байта для AVX, вы можете использовать 9X_memory-management posix_memalign:
void *buf = NULL; int err = posix_memalign( &buf, 32 /*alignment*/, 1024 /*size*/); if( err ) RunInCirclesWaivingArmsWildly(); ... free(buf);
Некоторые люди упоминали 9X_memory-usage интерфейс C++, который работает аналогично.
Не 9X_memory-usage следует забывать, что страницы выравниваются 9X_memory-allocation по большой степени двойки, поэтому буферы, выровненные 9X_memory-usage по страницам, также выравниваются по 16 9X_memory-deallocation байтам. Таким образом, mmap() и valloc() и 9X_memory-management другие подобные интерфейсы также являются 9X_memory-management опциями. У mmap() есть то преимущество, что 9X_c буфер может быть выделен предварительно 9X_memory-usage инициализированным с чем-то отличным от 9X_c нуля, если вы хотите. Поскольку они имеют 9X_memory-management размер, выровненный по странице, вы не получите 9X_memory-usage минимальное выделение из них, и, скорее 9X_memory-management всего, при первом прикосновении к ним может 9X_c произойти сбой виртуальной машины.
Cheesy: Включите 9X_memory-management охрану malloc или что-то подобное. Буферы 9X_memory-allocation размером n * 16 байт, такие как этот, будут 9X_memory-usage выровнены по n * 16 байтам, потому что виртуальная 9X_memory-allocation машина используется для обнаружения переполнений, а 9X_memory-management ее границы находятся на границах страницы.
Некоторые 9X_memory-usage функции Accelerate.framework используют 9X_memory-deallocation временный буфер, предоставляемый пользователем, для 9X_c использования в качестве рабочего места. Здесь 9X_c мы должны предположить, что переданный нам 9X_memory-usage буфер сильно рассредоточен, и пользователь 9X_memory-deallocation из злости активно пытается усложнить нашу 9X_memory-allocation жизнь. (Наши тестовые примеры прикрепляют 9X_memory-allocation защитную страницу прямо перед временным 9X_memory-management буфером и после него, чтобы подчеркнуть 9X_memory-allocation недовольство.) Здесь мы возвращаем минимальный 9X_c размер, который нам нужен, чтобы гарантировать 9X_c выравнивание 16-байтового сегмента где-нибудь 9X_c в нем, а затем вручную выравнивать буфер 9X_memory-management после этого. Это желаемый размер + выравнивание 9X_memory-deallocation - 1. Итак, в данном случае это 1024 + 16 9X_memory-allocation - 1 = 1039 байт. Затем выровняйте так:
#include void My_func( uint8_t *tempBuf, ... ) { uint8_t *alignedBuf = (uint8_t*) (((uintptr_t) tempBuf + ((uintptr_t)alignment-1)) & -((uintptr_t) alignment)); ... }
Добавление alignment-1 переместит указатель за первый выровненный адрес, а затем операция AND с -alignment (например, 0xfff ... ff0 для alignment = 16) вернет его к выровненному адресу.
Как 9X_memory-usage описано в других сообщениях, в других операционных 9X_memory-usage системах без гарантии 16-байтового выравнивания 9X_memory-allocation вы можете вызвать malloc с большим размером, отложить 9X_memory-management указатель для free() позже, затем выровнять, как 9X_c описано выше, и использовать выровненный 9X_c указатель , как описано для нашего случая 9X_memory-management временного буфера.
Что касается выровненного 9X_memory-management набора-мемов, это довольно глупо. Вам нужно 9X_memory-allocation всего лишь выполнить цикл до 15 байтов, чтобы 9X_memory-allocation достичь выровненного адреса, а затем продолжить 9X_memory-management с выровненными хранилищами после этого с 9X_memory-management некоторым возможным кодом очистки в конце. Вы 9X_memory-allocation даже можете выполнять очистку битов в векторном 9X_memory-management коде либо в виде невыровненных хранилищ, которые 9X_memory-deallocation перекрывают выровненную область (при условии, что 9X_memory-allocation длина равна по крайней мере длине вектора), либо 9X_memory-usage используя что-то вроде movmaskdqu. Кто-то 9X_memory-management просто ленился. Тем не менее, это, вероятно, разумный 9X_c вопрос для собеседования, если интервьюер 9X_memory-usage хочет знать, комфортно ли вам stdint.h, побитовые 9X_memory-deallocation операторы и основы памяти, поэтому надуманный 9X_memory-deallocation пример можно простить.
Ответ #10
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Использование memalign, Aligned-Memory-Blocks может быть хорошим 9X_memory-management решением проблемы.
- Обратите внимание, что в текущей (февраль 2016 г.) версии [указанной страницы] (http://www.gnu.org/s/libc/manual/html_node/Aligned-Memory-Blocks.html) говорится: «Функция memalign obsol ...
Ответ #11
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Первое, что пришло мне в голову при чтении 9X_memory-usage этого вопроса, - это определить выровненную 9X_memory-allocation структуру, создать ее экземпляр и затем 9X_memory-allocation указать на нее.
Есть ли какая-то фундаментальная 9X_memory-allocation причина, по которой я пропускаю, поскольку 9X_memory-management никто не предлагал этого?
В качестве примечания, поскольку 9X_memory-allocation я использовал массив символов (при условии, что 9X_memory-allocation системный символ составляет 8 бит (т.е. 1 9X_memory-allocation байт)), я не вижу необходимости в __attribute__((packed))
обязательно 9X_memory-management (поправьте меня, если я ошибаюсь ), но все 9X_memory-management равно вставил.
Это работает на двух системах, на 9X_memory-deallocation которых я это пробовал, но возможно, что 9X_memory-deallocation есть оптимизация компилятора, о которой 9X_memory-deallocation я не знаю, что дает мне ложные срабатывания 9X_c относительно эффективности кода. Я использовал 9X_memory-deallocation gcc 4.9.2
в OSX и gcc 5.2.1
в Ubuntu.
#include #include int main () { void *mem; void *ptr; // answer a) here struct __attribute__((packed)) s_CozyMem { char acSpace[16]; }; mem = malloc(sizeof(struct s_CozyMem)); ptr = mem; // memset_16aligned(ptr, 0, 1024); // Check if it's aligned if(((unsigned long)ptr & 15) == 0) printf("Aligned to 16 bytes.\n"); else printf("Rubbish.\n"); // answer b) here free(mem); return 1; }
Ответ #12
Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?
Для MacOS X:
- Все указатели, выделенные с помощью malloc, выровнены по 16 байт.
-
Поддерживается C11, поэтому 9X_memory-management вы можете просто вызвать align_malloc (16, size).
-
MacOS 9X_c X выбирает код, оптимизированный для отдельных 9X_memory-allocation процессоров, во время загрузки для memset, memcpy 9X_memory-deallocation и memmove, и этот код использует приемы, о 9X_memory-management которых вы никогда не слышали, чтобы сделать 9X_c его быстрым. Вероятность 99%, что memset 9X_memory-deallocation работает быстрее, чем любой написанный от 9X_memory-usage руки memset16, что делает весь вопрос бессмысленным.
Если 9X_memory-allocation вам нужно 100% портативное решение, до C11 9X_memory-allocation его не было. Потому что нет портативного 9X_memory-management способа проверить выравнивание указателя. Если 9X_c он не должен быть на 100% портативным, вы 9X_memory-management можете использовать
char* p = malloc (size + 15); p += (- (unsigned int) p) % 16;
Предполагается, что выравнивание 9X_memory-allocation указателя сохраняется в младших битах при 9X_memory-allocation преобразовании указателя в unsigned int. Преобразование 9X_c в unsigned int теряет информацию и определяется 9X_c реализацией, но это не имеет значения, потому 9X_memory-usage что мы не преобразуем результат обратно 9X_memory-deallocation в указатель.
Ужас, конечно, заключается в 9X_memory-usage том, что исходный указатель нужно где-то 9X_c сохранить, чтобы вызвать с ним free(). Так 9X_memory-management что в целом я бы действительно сомневался 9X_memory-deallocation в мудрости этого дизайна.
- Где в OS X вы находите «align_malloc»? Я использую Xcode 6.1, и ...
-
12
-
23
-
28
-
10
-
8
-
10
-
20
-
6
-
13
-
13
-
9
-
19
-
14
-
8
-
23
-
12
-
12
-
7
-
8
-
8
-
5
-
5
-
10
-
9
-
7
-
5
-
5
-
9
-
14
-
4
-
13
-
3
-
6
-
9
-
14
-
10
-
5
-
6
-
2
-
3
-
7
-
5
-
4
-
15
-
10
-
8
-
7
-
11
-
11
-
8