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

Это не приведет к конкуренции в кодовом гольф-соревновании и заимствует предыдущие ответы, но ясно показывает, как добавляется ключ и как работает соединение. Это создает 2 новых фрейма данных из списков, а затем добавляет ключ для декартового продукта.

Моим вариантом использования было то, что мне понадобился список всех идентификаторов магазина в течение каждой недели в моем списке. Итак, я создал список всех недель, которые я хотел иметь, а затем список всех идентификаторов хранилища, которые я хотел сопоставить им.

Слияние, которое я выбрал влево, но будет семантически таким же, как внутренний в этой настройке. Вы можете увидеть это в документации по слиянию , в которой говорится, что он имеет декартово произведение, если комбинация клавиш появляется более двух раз в обеих таблицах - это то, что мы установили.

days = pd.DataFrame({'date':list_of_days})
stores = pd.DataFrame({'store_id':list_of_stores})
stores['key'] = 0
days['key'] = 0
days_and_stores = days.merge(stores, how='left', on = 'key')
days_and_stores.drop('key',1, inplace=True)

400
задан legends2k 7 June 2019 в 02:26
поделиться

8 ответов

Исходный ответ

{
    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);
}

Объяснение, как требуется

первый шаг должен выделить достаточно запасного места на всякий случай. Так как память должна составить выровненных 16 байтов (подразумевать, что ведущий адрес байта должен быть кратным 16), добавляя, что 16 дополнительных байтов гарантируют, что у нас есть достаточно пространства. Где-нибудь в первых 16 байтах, существует выровненный указатель 16 байтов. (Обратите внимание, что malloc(), как предполагается, возвращает указатель, который является достаточно хорошо выровненный для [1 159] любой цель. Однако значение 'любого', прежде всего, для вещей как основные типы — long, double, long double, long long, и указатели на объекты и указатели на функции. Когда Вы делаете более специализированные вещи, как игра с графическими системами, им может быть нужно более строгое выравнивание, чем остальная часть системы — следовательно вопросы и ответы как это.)

следующий шаг должен преобразовать пустой указатель на символьный указатель; GCC, несмотря на это, Вы, как предполагается, не делаете адресную арифметику с указателями на пустых указателях (и GCC имеет предупреждение опций сказать Вам при злоупотреблении его). Тогда добавьте 16 к указателю запуска. Предположим malloc(), возвратил Вас невозможно плохо выровненный указатель: 0x800001. Добавление этих 16 дает 0x800011. Теперь я хочу округлить в меньшую сторону до 16-байтовой границы —, таким образом, я хочу сбросить последние 4 бита к 0. 0x0F имеет последний набор на 4 бита одному; поэтому, ~0x0F имеет весь набор битов одному кроме последних четырех. Выполнение операции "И", которое с 0x800011 дает 0x800010. Можно выполнить итерации по другим смещениям и видеть, что та же арифметика работает.

последний шаг, free(), легок: Вы всегда, и только, возвращаетесь к [1 114] значение, что один из [1 115], calloc() или realloc() возвратил Вам —, что-либо еще - бедствие. Вы правильно обеспечили mem для содержания того значения — спасибо. Бесплатные выпуски это.

Наконец, если Вы знаете о внутренностях своей системы malloc пакет, Вы могли бы предположить, что это могло бы хорошо возвратиться, 16 байтов выровняли данные (или это могли бы быть выровненных 8 байтов). Если бы это были выровненных 16 байтов, то Вы не должны были бы укорачивать со значениями. Однако это - изворотливый и непортативный — другой malloc, пакеты имеют различное минимальное выравнивание и поэтому принятие одной вещи, когда это делает что-то другое привело бы к дампам ядра. В широких пределах это решение является портативным.

Кто-то еще упомянул posix_memalign() как другой способ получить выровненную память; это не доступно везде, но могло часто реализовываться с помощью этого в качестве основания. Обратите внимание, что было удобно, что выравнивание было питанием 2; другое выравнивание более грязно.

Еще один комментарий — этот код не проверяет что выделение, за которым следуют.

Windows Programmer Поправки

указал, что Вы не можете сделать операций битовой маски на указателях, и, действительно, GCC (3.4.6 и 4.3.1 протестированных) действительно жалуется как этот. Так, исправленная версия абсолютного кода — преобразованный в основную программу, следует. Я также брал на себя смелость добавления всего 15 вместо 16, как был указан. Я использую uintptr_t, так как C99 был вокруг достаточно долго, чтобы быть доступным на большинстве платформ. Если бы это не было для использования [1 123] в эти printf() операторы, это было бы достаточно к [1 125] вместо того, чтобы использовать #include <inttypes.h>. [Этот код включает фиксацию, на которую указывают [1 151] C.R. , который повторял мнение, сначала высказанное [1 152] счет K много лет назад, который мне удалось пропустить до сих пор.]

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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);
}

И вот незначительно более обобщенная версия, которая будет работать на размеры, которые являются питанием 2:

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

проблемы с интервьюерами

прокомментированный Uri: Возможно, у меня есть проблема понимания прочитанного этим утром, но если вопрос об интервью конкретно говорит: "Как Вы выделили бы 1 024 байта памяти", и Вы ясно выделяете больше, чем это. Это не было бы автоматическим отказом от интервьюера?

Мой ответ не впишется в комментарий с 300 символами...

Это зависит, я предполагаю. Я думаю, что большинство людей (включая меня) ответило на вопрос для значения, "Как Вы выделите место, в котором 1 024 байта данных могут быть сохранены, и где базовый адрес является кратным 16 байтам". Если интервьюер действительно имел в виду, как можно выделить 1 024 байта (только) и иметь его выровненных 16 байтов, то опции более ограничены.

  • Очевидно, одна возможность состоит в том, чтобы выделить 1 024 байта и затем предоставить тому адресу 'лечение выравнивания'; проблема с тем подходом состоит в том, что фактическое свободное место не является правильно определенным (применимое пространство между 1008 и 1 024 байтами, но не было механизма, доступного для определения, какой размер), который представляет его менее, чем полезный.
  • Другая возможность состоит в том, что Вы, как ожидают, запишете полное средство выделения памяти и удостоверитесь, что 1 024-байтовый блок, который Вы возвращаете, соответственно выровненный. Если это так, Вы, вероятно, заканчиваете тем, что делали операцию, довольно подобную тому, что сделало предлагаемое решение, но Вы скрываете его в средстве выделения.

Однако, если бы интервьюер ожидал любой из тех ответов, я ожидал бы, что они распознают, что это решение отвечает на тесно связанный вопрос, и затем повторно структурировать их вопрос указать на разговор в корректном направлении. (Далее, если бы интервьюер добрался действительно stroppy, то я не хотел бы задания; если ответ на недостаточно точное требование подстрелен в провокационных сообщениях без исправления, то интервьюер не кто-то, на кого безопасно работать.)

мировые перемещения на [1 168]

заголовок вопроса недавно изменился. Это было , Решают выравнивание памяти в вопросе об интервью C, который озадачил меня . Пересмотренный заголовок (, Как выделить выровненную память, только пользующуюся стандартной библиотекой? ), требует, чтобы немного пересмотренный ответ — это приложение обеспечил его.

C11 (ISO/IEC 9899:2011) добавленная функция aligned_alloc():

7.22.3.1 aligned_alloc функция

Описание
Резюме

#include <stdlib.h>
void *aligned_alloc(size_t alignment, size_t size);

Эти aligned_alloc функция выделяет место для объекта, выравнивание которого определяется [1 131], чей размер определяется [1 132], и чье значение неопределенно. Значение [1 133] должно быть допустимым выравниванием, поддерживаемым реализацией, и значение [1 134] должно быть интегралом несколько из [1 135].

Возвраты
aligned_alloc функция возвращает или нулевого указателя или указатель на выделенное место.

И POSIX определяет posix_memalign() :

#include <stdlib.h>

int posix_memalign(void **memptr, size_t alignment, size_t size);

ОПИСАНИЕ

Эти posix_memalign() функция должна выделить size байты, выровненные на границе, определенной [1 140], и должна возвратить указатель на выделенную память в [1 141]. Значение [1 142] должно быть питанием два несколько из [1 143].

После успешного завершения, значение, на которое указывают [1 144], должно быть кратным [1 145].

, Если размер пространства, которое требуют, 0, поведение определяется реализацией; значение, возвращенное в [1 146], должно быть или нулевым указателем или уникальным указателем.

Эти free() функция должна освободить память, которая была ранее выделена [1 148].

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

После успешного завершения, posix_memalign() должно возвратить нуль; иначе код ошибки должен быть возвращен для указания на ошибку.

Или или оба из них мог использоваться для ответа на вопрос теперь, но только функция POSIX была опцией, когда на вопрос первоначально ответили.

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

565
ответ дан Community 7 June 2019 в 02:26
поделиться
  • 1
    Потоки C++ действительно являются одним из моих главных объектов неприязни. О скорости atoi по сравнению с stringstreams. Вы пишете " Насколько быстрее? Не намного " Обычно варианты C являются, по крайней мере, порядком величины быстрее. Поэтому доверяйте мне, это может иметь значение. Другой недостаток является временем компиляции - внезапно, компилятор должен втянуть и проанализировать сотни килобайтов шаблона кода, который может иметь значение в крупном проекте. Для добавления оскорбления травмы эквивалентный код C++ обычно более сложен также. – Johan Kotlinski 13 November 2010 в 10:50

Возможно, они были бы удовлетворены знанием memalign? И поскольку Jonathan Leffler указывает, существует две более новых предпочтительных функции для знания о.

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

8
ответ дан Don Wakefield 7 June 2019 в 02:26
поделиться
  • 1
    Большое спасибо за Ваш полный ответ! Принятый и отмеченный – Kiril Kirov 13 November 2010 в 13:55

Вот альтернативный подход к вокруг' части. Не наиболее блестяще кодированное решение, но это сделало задание, и этот тип синтаксиса немного легче помнить (плюс, работал бы на значения выравнивания, которые не являются питанием 2). Эти uintptr_t бросок был необходим для успокоения компилятора; адресная арифметика с указателями не очень любит разделение или умножение.

void *mem = malloc(1024 + 15);
void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16;
memset_16aligned(ptr, 0, 1024);
free(mem);
19
ответ дан An̲̳̳drew 7 June 2019 в 02:26
поделиться
  • 1
    Я создал использование класса PageAdapter. Проверьте мою программу. Я обновил это также... и помогите мне теперь... –  7 September 2012 в 11:46

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

37
ответ дан Jonathan Leffler 7 June 2019 в 02:26
поделиться
  • 1
    я установил библиотеку, как Вы сказали. Но в Project-> нет никакого viewpagerindicator; Свойства-> Android-> Прокрутите вниз к разделу Library, нажмите Add... –  7 September 2012 в 12:27

Три немного отличающихся ответа, зависящие, как Вы смотрите на вопрос:

1) Достаточно хороший для точного вопроса, который задают, решение Jonathan Leffler, за исключением того, что окружить к 16 выровненному, Вам только нужны 15 дополнительных байтов, не 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;

B:

free(mem);

2) Для более универсальной функции выделения памяти, вызывающая сторона не хочет должной быть отслеживать два указателя (один для использования и один для свободного). Таким образом, Вы храните указатель на 'реальный' буфер ниже выровненного буфера.

А:

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;

B:

if (ptr) free(((void**)ptr)[-1]);

Примечание, что, в отличие от этого (1), где только 15 байтов были добавлены к мадам, этот код мог на самом деле , уменьшает выравнивание, если Ваша реализация, оказывается, гарантирует 32-байтовое выравнивание от malloc (вряд ли, но в теории реализация C могла иметь выровненный тип 32 байтов). Это не имеет значения, ли все, что Вы делаете, назвать memset_16aligned, но если Вы используете память для структуры тогда, это могло бы иметь значение.

я не уверен пренебрежительный, что хорошая фиксация для этого (кроме предупредить пользователя, что возвращенный буфер не обязательно подходит для произвольных структур), так как нет никакого способа определить программно, какова определенная для реализации гарантия выравнивания. Я предполагаю запуск, Вы могли выделить два или больше 1-байтовых буфера и предположить, что худшее выравнивание, которое Вы видите, является гарантируемым выравниванием. Если Вы неправы, Вы тратите впустую память. Любой с лучшей идеей, скажите так...

[ Добавленный : 'стандартный' прием должен создать объединение, 'вероятно, чтобы быть максимально выровненным типы' для определения необходимого выравнивания. Максимально выровненные типы, вероятно, будут (в C99) 'long long', 'long double', 'void *', или' void (*)(void)'; если Вы включаете <stdint.h>, Вы могли бы, по-видимому, использовать' intmax_t' вместо [1 111] (и, на Питании 6 (AIX), машины, intmax_t дадут Вам 128-разрядный целый тип). Требования выравнивания для того объединения могут быть определены путем встраивания его в структуру с единственным символом, сопровождаемым объединением:

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;

Вы тогда использовали бы большее из требуемого выравнивания (в примере, 16) и align значение, вычисленное выше.

На (64-разрядном) Солярисе 10, кажется, что основное выравнивание для результата malloc() является кратным 32 байтам.
]

На практике, выровненные средства выделения часто берут параметр для выравнивания, а не этого быть предрасположенное. Таким образом, пользователь передаст в размере структуры, о которой они заботятся (или наименьшее количество питания 2 больших, чем или равный этому), и все будут хорошо.

3) Использование, что обеспечивает Ваша платформа: posix_memalign для POSIX, _aligned_malloc в Windows.

4) при использовании C11, тогда самое чистое - портативный и краткий - опция состоит в том, чтобы использовать стандартную библиотечную функцию aligned_alloc , которая была представлена в этой версии спецификации языка.

56
ответ дан 12 revs, 3 users 73% 7 June 2019 в 02:26
поделиться
  • 1
    я хотел создать какой-либо класс от имени TitlePageIndicator или какой-либо другой класс?? –  7 September 2012 в 12:28
size =1024;
alignment = 16;
aligned_size = size +(alignment -(size %  alignment));
mem = malloc(aligned_size);
memset_16aligned(mem, 0, 1024);
free(mem);

Hope этот - самая простая реализация, сообщите мне свои комментарии.

0
ответ дан 22 November 2019 в 23:35
поделиться

На фронте заполнения 16 и 15 байтов фактическое число, которое вам нужно добавить, чтобы получить выравнивание N, составляет max (0, NM) , где M - естественное выравнивание распределителя памяти (и оба являются степенями 2).

Поскольку минимальное выравнивание памяти любого распределителя составляет 1 байт, 15 = max (0,16-1) - консервативный ответ. Однако, если вы знаете, что ваш распределитель памяти будет выдавать вам 32-битные адреса, выровненные по int (что довольно часто), вы могли бы использовать 12 в качестве контактной площадки.

Это не важно для данного примера, но может быть важно для встроенной системы с 12 КБ ОЗУ, где учитывается каждый сохраненный int.

Лучший способ реализовать его, если вы действительно собираетесь сохранить каждый возможный байт, - это использовать макрос, чтобы вы могли передать ему свою собственную память выравнивание. Еще раз, это, вероятно, полезно только для встроенных систем, где вам нужно сохранить каждый байт.

В приведенном ниже примере для большинства систем значение 1 вполне подходит для MEMORY_ALLOCATOR_NATIVE_ALIGNMENT , однако для нашей теоретической встроенной системы с 32-битное выравнивание, следующее может сэкономить крошечный бит драгоценной памяти:

#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
ответ дан 22 November 2019 в 23:35
поделиться

К сожалению, в C99 довольно сложно гарантировать любое выравнивание таким образом, чтобы его можно было переносить в любую реализацию C, соответствующую C99. Почему? Поскольку указатель не обязательно является «байтовым адресом», это можно представить с помощью плоской модели памяти. Также не гарантируется представление uintptr_t , которое в любом случае является необязательным типом.

Возможно, мы знаем о некоторых реализациях, которые используют представление для void * (и по определению также char * ), который является простым байтовым адресом, но для C99 он непрозрачен. нам, программистам. Реализация может представлять указатель с помощью набора { сегмента , смещения }, где смещение могло иметь неизвестное выравнивание «в действительности». Почему, указатель может даже быть некоторой формой значения поиска в хеш-таблице или даже значением поиска в связанном списке. Он мог кодировать информацию о границах.

В недавнем черновике C1X для стандарта C мы видим ключевое слово _Alignas . Это может немного помочь.

Единственная гарантия, которую дает нам C99, заключается в том, что функции выделения памяти будут возвращать указатель, подходящий для присвоения указателю, указывающему на любой тип объекта. Поскольку мы не можем указать выравнивание объектов, мы не можем реализовать наши собственные функции распределения, отвечающие за выравнивание, четко определенным переносимым способом.

Было бы хорошо ошибиться в этом утверждении.

19
ответ дан 22 November 2019 в 23:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: