Как управлять выравниванием памяти и универсальной арифметикой указателей в портативном виде в C? - PullRequest
5 голосов
/ 23 января 2012

Мне нужно реализовать оптимизированную версию malloc / realloc / free (специально для моего приложения).На данный момент код выполняется на конкретной платформе, но я хотел бы написать его переносимым способом, если это возможно (платформа может измениться в будущем), или, по крайней мере, я бы хотел сконцентрировать возможные различия платформы в однойточка (вероятно, .h).Мне известны некоторые проблемы:

  • различия в выравнивании памяти
  • различия в наименьших размерах блоков памяти, подходящих для "общего" выделения
  • различия в размере указателя

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

Вопрос (ы):

  • Существуют ли в C стандартные макросы или функции для такого рода целей?
  • ЧтоМогу ли я столкнуться с другими проблемами на этой работе?

Ответы [ 6 ]

3 голосов
/ 23 января 2012

Классический способ убедиться, что вы поддерживаете выравнивание, подходящее для всех основных типов, - это определить объединение:

union alloc_align {
    void *dummy1;
    long long dummy2;
    long double dummy3;
};

... затем убедитесь, что адреса, которые вы раздаете, всегда смещаются на несколькоsizeof (union alloc_align) от выровненных адресов, которые вы получаете от распределителя системной памяти.

Я полагаю, что метод, подобный этому, описан в K & R.

1 голос
/ 23 января 2012

Основная проблема заключается в том, что вы предоставляете только общий размер блока памяти malloc() и друзьям, без какой-либо информации о гранулярности объекта. Если вы рассматриваете выделение как массив объектов, то у вас есть размер, равный размеру базового объекта, и число n, равное количеству объектов в массиве, например ::10000

p = malloc(sizeof(*p) * n);

Если у вас есть только общий размер, то вы не знаете, если s = 4 и n = 10, или если s = 2 и n = 20, или s = 1 и n = 40, потому что все умножаются на общий размер 40 байт.

Итак, основной вопрос в том, хотите ли вы прямую замену оригинальным функциям, например, когда вы перебрасываете собственные вызовы по всей вашей кодовой базе или у вас есть централизованная и СУХАЯ модульность с функциями-обертками. Там вы можете использовать функции, которые предоставляют s и n.

void *my_malloc (size_t s, size_t n)

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

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

1 голос
/ 23 января 2012

Функции выравнивания обрабатываются только в новом стандарте C, C11. Он имеет ключевые слова _Alignof, _Alignas и функцию aligned_alloc. Эти функции не очень сложно эмулировать с большинством современных компиляторов (как указано в других ответах), поэтому я бы посоветовал вам написать небольшие макросы или оболочки, которые вы будете использовать в зависимости от __STDC_VERSION__.

1 голос
/ 23 января 2012
Выравнивание

памяти отличается от компилятора к сожалению (это одна из проблем), в MSVC у вас есть aligned_malloc, у вас также есть POSIX memalign для Linux, а затем есть _mm_alloc который работает в соответствии с ICC, MSVC и GCC, IIRC, который должен быть наиболее переносимым.

Вторая проблема - это потеря памяти при выравнивании, это не будет серьезной проблемой, но во встроенных системах это то, на что стоит обратить внимание.

если вы размещаете в стеке вещи, требующие выравнивания (например, SIMD-типы), вы также хотите изучить __attribute__((__aligned__(x))) и __declspec(align(x)).

с точки зрения переносимости арифметики указателей, вы можете использовать типы из stdint.h / pstdint.h, чтобы сделать это, но стандарты могут что-то сказать об UB при приведении между uintptr_t и указателем (к сожалению, стандарты не являются ' Моя сильная сторона :().

0 голосов
/ 23 января 2012

C говорит: malloc возвращает указатель на память, выровненную для любой цели. В C нет портативного способа добиться этого с помощью функций C. Это приводит к тому, что malloc - это функция, которая, если она написана на C, не может быть записана переносимым способом.

(C99, 7.20.3p1) "Указатель, возвращаемый в случае успешного выделения, соответствующим образом выравнивается, чтобы его можно было присвоить указателю на объект любого типа, а затем использовать для доступа к такому объекту или массиву таких объектов в выделенное пространство (до тех пор, пока пространство не будет явно освобождено). "

0 голосов
/ 23 января 2012

Если вы посмотрите на #pragma pack, это может вам помочь, поскольку оно позволяет вам определять структуру структуры и реализовано на большинстве компиляторов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...