Использование realloc в c ++ - PullRequest
15 голосов
/ 03 ноября 2010

std::realloc опасно в c ++, если память malloc содержит типы не-pod.Кажется, проблема only состоит в том, что std::realloc не вызовет деструкторы типов, если не может увеличить память in situ.

Тривиальным обходным решением будет функция try_realloc.Вместо того, чтобы malloc'ing новую память, если она не может быть выращена на месте, она просто вернет false.В этом случае может быть выделена новая память, объекты скопированы (или перемещены) в новую память и, наконец, старая память освобождена.

Это кажется чрезвычайно полезным.std::vector мог бы использовать это, возможно, избегая всех копий / перераспределений.
превентивный антипирен: Технически, это та же производительность Big-O, но если рост векторов является узким местом в вашемПриложение ускорение x2 хорошо, даже если Big-O остается неизменным.

НО, я не могу найти какой-либо API, который работает как try_realloc.

Я что-то упустил?try_realloc не так полезен, как я себе представляю?Есть ли какая-то скрытая ошибка, которая делает try_realloc непригодной для использования?

Еще лучше, есть ли менее документированный API, который работает как try_realloc?

ПРИМЕЧАНИЕ: I 'м, очевидно, в библиотеке / платформе специфический код здесь.Я не волнуюсь, так как try_realloc по сути является оптимизацией.


Обновление: После комментария Стива Джессопса о том, будет ли vector более эффективным при использовании realloc, я написалдоказательство концепции для тестирования.realloc-vector имитирует модель роста вектора, но вместо этого имеет возможность перераспределения.Я запустил программу до миллиона элементов в векторе.

Для сравнения vector должен выделять 19 раз при увеличении до миллиона элементов.

Результаты, если realloc-vectorэто единственная вещь, использующая кучу, результаты потрясающие, 3-4 выделения при увеличении до размера в миллион байт.

Если realloc-vector используется вместе с vector, который растет со скоростью 66%из realloc-vector Результаты менее многообещающие, выделяя 8-10 раз во время роста.

Наконец, если realloc-vector используется вместе с vector, который растет с той же скоростью, realloc-vectorвыделяет 17-18 раз.Едва экономя одно выделение по сравнению со стандартным векторным поведением.

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

Ответы [ 3 ]

11 голосов
/ 03 ноября 2010

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

Так что я думаю, что для того, чтобы получить действительно хорошую оптимизацию здесь, вам нужно больше, чем «тривиальный обходной путь», который делает дешевое перераспределение, если это возможно, - вы должны как-то подготовиться к make это возможно, и эта подготовка обходится вам в адресное пространство.Если вы делаете это только для определенных векторов, которые указывают, что они станут большими, то это довольно бессмысленно, поскольку они могут указать reserve(), что они станут большими.Вы можете сделать это автоматически для всех векторов, только если у вас есть обширное адресное пространство, так что вы можете «тратить» большую его часть на каждый вектор.

Насколько я понимаю, причина в Allocator Концепция не имеет функции перераспределения, чтобы сохранить его простым.Если бы у std::allocator была функция try_realloc, то либо у каждого Allocator должна была бы быть одна (которая в большинстве случаев не могла быть реализована, и она всегда должна была бы возвращать false), либо каждый стандартный контейнер должен был бы бытьспециализируется на std::allocator, чтобы воспользоваться этим.Ни одна из этих опций не является отличным интерфейсом Allocator, хотя я полагаю, что для разработчиков почти всех классов Allocator не потребовалось бы больших усилий, чтобы просто добавить функцию «ничего не делать» try_realloc.

Если vector медленныйиз-за перераспределения, deque может быть хорошей заменой.

4 голосов
/ 03 ноября 2010

Вы можете реализовать что-то вроде предложенного try_realloc, используя mmap с MAP_ANONYMOUS и MAP_FIXED и mremap с MREMAP_FIXED.

Редактировать :только что заметил, что man-страница для mremap даже говорит:

mremap () использует схему таблицы страниц Linux.mremap () изменяет отображение между виртуальными адресами и страницами памяти.Это может быть использовано для реализации очень эффективного realloc (3).

2 голосов
/ 03 ноября 2010

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

Пока вы не будете постоянно перераспределять массив каждый раз, когда добавляете элемент, а вместо этого увеличиваете массив экспоненциально (например, на 25%, 50% или100%) всякий раз, когда вам не хватает места, простое ручное выделение новой памяти, копирование и освобождение старой даст примерно ту же (и идентичную, в случае фрагментации памяти) производительность при использовании realloc.Это, безусловно, подход, который используют реализации C ++ STL, поэтому я думаю, что вся ваша озабоченность необоснованна.

Edit : один (редкий, но не неслыханный) случай, когда reallocна самом деле полезно для гигантских блоков в системах с виртуальной памятью, где библиотека C взаимодействует с ядром, чтобы переместить целые страницы на новые адреса.Причина, по которой я говорю, что это редко, в том, что вам нужно иметь дело с очень большими блоками (по крайней мере, несколько сотен килобайт), прежде чем большинство реализаций даже войдут в сферу работы с распределением гранулярности страницы, и, вероятно, намного больше (возможно, несколько МБ)перед входом и выходом из пространства ядра переставить виртуальную память дешевле, чем просто сделать копию.Конечно, try_realloc здесь не будет полезен, поскольку вся выгода заключается в том, что на самом деле делает ход недорого.

...