Перемещение объектов C ++, особенно контейнеров stl, в определенную область памяти - PullRequest
2 голосов
/ 11 февраля 2010

Я работаю с менеджером памяти, который иногда хочет дефрагментировать память. По сути, я рассмотрю список объектов, выделенных диспетчером памяти, и переместу их:

class A {
  SomeClass* data; // This member is allocated by the special manager
};

for(... each instance of A ...) 
    a.data = memory_manager.relocate(a.data);

memory_manager.relocate() доставит memcpy() содержимое данных в новое место и вернет указатель.

Хотя это, как правило, идиоматично для memcpy() классов C ++, в этом случае представляется полезным решением, учитывая, что я контролирую реализацию (нескольких) классов, которые будут использоваться с менеджером памяти.

Проблема в том, что один из этих классов использует std::map, насколько я понимаю, непрозрачный класс. Я, конечно, не представляю, что смогу memcpy(). Я не могу использовать std :: map в любом случае. Насколько я знаю, он мог бы выделить несколько частей памяти.

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

for(... each instance of A ...) {
    stl::map<something>* tmp = a.the_map;
    a.the_map = new stl::map<something>(tmp);
    delete tmp;
}



В любом случае это заставляет меня задуматься:

Есть ли в C ++ семантика или идиомы для перемещения / копирования объекта в определенную область памяти?

Можно ли переместить содержимое контейнера stl в определенную область памяти?


Изменить: Хотя я не указал на это, я бы, очевидно, передал параметр распределителя в std :: map. Основываясь на информативных ответах, которые я получил, я понимаю, что обходной путь, который я разместил в своем первоначальном вопросе, вероятно, будет единственным способом уменьшить фрагментацию. Используя конструктор копирования карты (и параметр шаблона распределителя), вся память, используемая картой, будет правильно перераспределена.

Как отмечается в комментарии, это в основном теоретическая проблема. Фрагментация памяти редко вызывает беспокойство.

Ответы [ 6 ]

4 голосов
/ 11 февраля 2010

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

По умолчанию при создании карты, как в std::map<K,V>, используется распределитель по умолчанию, который создает узлы в куче (т. Е. С new / delete).

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

Создание класса распределителя не тривиально. Этот код показывает, как это можно сделать, вам придется адаптировать его под свои нужды.

Как только вы получите свой класс распределителя (скажем, вы называете его MemManagerAllocator), вам нужно будет определить свою карту как std::map<K, V, MemManagerAllocator>, а затем использовать ее так, как если бы вы использовали обычную карту.

Лично мне понадобится действительно серьезная проблема фрагментации памяти, чтобы разобраться со всеми этими проблемами.

2 голосов
/ 11 февраля 2010

Вы могли бы использовать новое размещение?

void* adress = new void[size_of(*old_map_ptr)]; // or wherever you want it in your memory
map<type> new_map* = new (adress) map<type>(*old_map_ptr);
delete old_map_ptr;
2 голосов
/ 11 февраля 2010

Содержимое объекта C ++ будет разбросано по куче. Объект STL, например контейнер или строка и т. Д., Может хранить свои метаданные в вашем стеке (если вы не помещаете его в динамическую память) ... однако содержимое разбросано по куче.

Будет трудно отследить все элементы объекта и все ссылки на эти элементы. Язык должен предоставлять некоторую форму «хуков» или событий, позволяющих отслеживать отношения между частями памяти.

Так что нет, вы не можете просто запоминать произвольный объект STL.

Переопределение нового Afaik - это не панацея, это концепция "глобального процесса", поэтому попытка локализовать новости в паре поток-объект-размещение невозможна. И эта локализация позволит вам сгруппировать память вместе.

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

1 голос
/ 11 февраля 2010

Нет, C ++ управляет памятью исключительно самостоятельно, но вы можете помочь stl сделать это, внедрив std :: map :: allocator, который сделает все так, чтобы вам не понадобился memcpy.

0 голосов
/ 11 февраля 2010

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

0 голосов
/ 11 февраля 2010

Вы не упомянули, для какой платформы вы кодируете или разрешено ли это иметь значение. Но в Linux и GCC распределитель C ++ по умолчанию реализован с помощью malloc.

malloc может быть переопределено и заменено вашим собственным внутренним распределителем.

Я бы реализовал это malloc следующим образом:

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

Итак, перед созданием этого конкретного std::map вы дадите подсказку своему malloc. (Через глобальную переменную или через какое-либо другое средство связи.)

Обновление: alemjerus предложил реализовать std::map::allocator, который является намного более чистым способом на ту же тему, также независимым от платформы.

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