Действительно ли realloc сокращает буферы в общих реализациях?

В обычных реализациях, таких как Linux/Glibc, Windows/MSVC и BSD/Mac OS X, будет

void *p = malloc(N + M);  // assume this doesn't fail
p = realloc(p, N);        // nor this

для N, M > 0, фактически сократите буфер, возвращенный malloc в вызове realloc, в том смысле, что до M байты могут вернуться в свободный список? И что еще более важно, есть ли вероятность перераспределения буфера?

Я хочу знать, потому что я только что реализовал динамические массивы поверх numpy.ndarray, и я делаю resize, который вызывает realloc, чтобы получить конечный размер. Я могу пропустить окончательный resize как оптимизацию (за счет постоянной переписи), и я хочу знать, стоит ли даже попробовать.

Ответ 1

Я могу сказать о Linux/glibc. В исходном коде содержатся следующие комментарии:

если n для меньшего количества байтов, чем уже удерживается p, вновь неиспользованный
 пространство отрывается и, если возможно, освобождается.

если вы посмотрите код glibc, он содержит такие строки:

remainder_size = newsize - nb;

if (remainder_size < MINSIZE) { /* not enough extra to split off */
  set_head_size(newp, newsize | (av != &main_arena ? NON_MAIN_ARENA : 0));
  set_inuse_bit_at_offset(newp, newsize);
}
else { /* split remainder */
  remainder = chunk_at_offset(newp, nb);
  set_head_size(newp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0));
  set_head(remainder, remainder_size | PREV_INUSE |
       (av != &main_arena ? NON_MAIN_ARENA : 0));
  /* Mark remainder as inuse so free() won't complain */
  set_inuse_bit_at_offset(remainder, remainder_size);
 #ifdef ATOMIC_FASTBINS
  _int_free(av, remainder, 1);
 #else
  _int_free(av, remainder);
 #endif
}

nb - количество байтов, которое вы хотите, newsize здесь, должно называться oldsize. Поэтому он пытается освободить избыток, если это возможно.

О Mac OSX. Точнее о magazine_malloc, текущей реализации malloc от Apple. Подробнее см. http://cocoawithlove.com/2010/05/look-at-how-malloc-works-on-mac.html.

realloc вызывает метод realloc зоны, его текущая реализация, как я вижу, szone_realloc. Для разных размеров размещения существует другой код, но алгоритм всегда один и тот же:

if (new_good_size <= (old_size >> 1)) {
            /*
             * Serious shrinkage (more than half). free() the excess.
             */
            return tiny_try_shrink_in_place(szone, ptr, old_size, new_good_size);
} else if (new_good_size <= old_size) {
            /* 
             * new_good_size smaller than old_size but not by much (less than half).
             * Avoid thrashing at the expense of some wasted storage.
             */
             return ptr;
}

Итак, как вы можете видеть, его реализация проверяет, что new_size <= old_size / 2, и если это освобождает память, а если нет, то ничего не делает.

Ответ 2

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

Общие распределители памяти обычно предполагают, что вызывающий абонент знает предыдущий размер блока и будет называться только realloc, если они действительно знали, что они хотят сжать блок. Последний, на который я смотрел, был готов сжать блок, если блок уже превысил 128 байт, и перераспределение позволит освободить как минимум 1 Кбайт или, по крайней мере, количество байтов, равное 1/4 от размера выделения текущего блока. Он был настроен для серверных приложений большого объема, где объекты обычно не поддерживаются очень долго и где для объектов, которые, как известно, существуют очень долгое время, была предложена специальная операция "правильного размера".

Ответ 3

Да, да. Но стандарт ничего не говорит, что заставляет это. Поэтому вы должны проверить его с помощью libc, на который настроен таргетинг. Вы можете сделать это, посмотрев код, если он доступен, или написав тестовую программу. Идея тестовой программы такова: вы выделяете относительно большой блок (скажем, 10K), а затем пытаетесь сжать с помощью realloc и выделить что-то маленькое с помощью malloc. Если вновь возвращенный адрес лежит в диапазоне, который вы выделили в первый раз, ваш realloc сжимается, в противном случае это не так.