Как выделить выровненную память только с помощью стандартной библиотеки?

Я только что прошел тест в рамках собеседования, и 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-management memset_16aligned.

{ void *mem; void *ptr; // answer a) here memset_16aligned(ptr, 0, 1024); // answer b) here } 

464
8

  • @ StevenA.Lowe Существуют допустимые варианты использования, требующие 16-байтовой выровненной памяти, когда вы хотите, чтобы используемый код мог работать _ действительно быстро_ без дополнительных проверок. Графический фреймворк Apple Me ...
12
Общее количество ответов: 12

Ответ #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-usage alignment, размер которого задается 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-deallocation alignment должно быть степенью двойки, кратной 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 функцией распределения.

632
15

  • Просто из любопытства, `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 этой версии спецификации языка.

62
6

  • Я возражаю против использования ASSERT (mem); для проверки результатов распределения; `assert` предназначен для выявления ошибок программирования ...

Ответ #3

Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?

Вы также можете попробовать posix_memalign() (конечно, на 9X_memory-management платформах POSIX).

40
2

  • И _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); 

20
1

  • В общем, если у вас есть '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 утверждении.

20
1

  • В 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) 

15
0

Ответ #7

Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?

Может быть, им понравилось бы знать memalign? И, как 9X_memory-allocation указывает Джонатан Леффлер, есть две более 9X_memory-deallocation новые функции, о которых следует знать.

Ой, Флорин 9X_memory-allocation меня опередил. Однако, если вы прочитаете 9X_c страницу руководства, на которую я ссылаюсь, вы, скорее 9X_memory-deallocation всего, поймете пример, представленный на 9X_memory-deallocation более раннем постере.

9
1

  • Обратите внимание, что в текущей (февраль 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 обратно.)

5
0

Ответ #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 пример можно простить.

5
0

Ответ #10

Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?

Использование memalign, Aligned-Memory-Blocks может быть хорошим 9X_memory-management решением проблемы.

4
1

  • Обратите внимание, что в текущей (февраль 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; } 

3
0

Ответ #12

Ответ на вопрос: Как выделить выровненную память только с помощью стандартной библиотеки?

Для MacOS X:

  1. Все указатели, выделенные с помощью malloc, выровнены по 16 байт.
  2. Поддерживается C11, поэтому 9X_memory-management вы можете просто вызвать align_malloc (16, size).

  3. 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 в мудрости этого дизайна.

1
1

  • Где в OS X вы находите «align_malloc»? Я использую Xcode 6.1, и ...