Действительно ли realloc сжимает буферы в общих реализациях? - PullRequest
11 голосов
/ 18 ноября 2011

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

Ответы [ 3 ]

16 голосов
/ 18 ноября 2011

Я могу сказать о 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, и если да, то освобождает память, а если нет, то делаетнет ничего.

4 голосов
/ 18 ноября 2011

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

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

0 голосов
/ 18 ноября 2011

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

...