Лучшие контейнеры STL, чтобы избежать фрагментации кучи

У меня есть программа, которая анализирует 150 000 файлов. Valgrind не сообщает об утечке памяти, но программа со временем замедляется.

Некоторые проблемы были связаны со слишком частым использованием std::string и слишком длительным использованием mktime. (см. С++ замедляет чтение 70 000 файлов)

Но со временем все равно тормозит. Lotharyx предположил, что использование контейнера вызывает фрагментацию кучи.

Я читал различные блок-схемы плюсов и минусов различных контейнеров STL, но не совсем понял.

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

fileList.clear()
scan all disks and build "fileList", a std::set of file paths matching a pattern.

// filepaths are named by date, eg 20160530.051000, so are intrinsically ordered 

foreach(filePath in fileList)
{
    if (alreadyHaveFileDetails(filePath))
        continue;

    // otherwise
    collect file details into a fileInfoStruct;  // like size, contents, mod 

    fileInfoMap[time_t date] = fileInfoStruct;
}

// fileInfoMap is ordered collection of information structs of about 100,000 files

// traverse the list in order
foreach (fileInfo in fileInfoMap)
{
    if (meetsCondition(fileInfo))
    {
        TEventInfo event = makeEventInfo()
        eventList.push_back(event);
    }
}

И вышеописанная последовательность повторяется вечно.

Итак, для выбора контейнеров я использовал (или нуждался):

fileList -- список уникальных строк, содержащий 150 000 путей.
Я выбрал std::set, потому что он автоматически обрабатывает дубликаты и автоматически поддерживает порядок сортировки. Никакого произвольного доступа, только добавляйте записи, сортируйте их (вручную или автоматически) и перебирайте их.

fileInfoMap -- массив структур с отметкой времени time_t, соответствующей дате файла. Я выбрал std::map. Он также будет иметь 150 000 записей, поэтому занимает много памяти. Никакого произвольного доступа, добавляйте записи только в один конец. Необходимо перебирать их и при необходимости удалять записи из середины.

eventList -- небольшой список "событийных" структур, скажем, 50 элементов. Я выбрал std::vector. Не уверен, почему на самом деле. Никакого произвольного доступа, добавляйте записи только в один конец, а затем перебирайте коллекцию.

Я довольно новичок в C++. Спасибо за внимание.


person Danny    schedule 04.07.2016    source источник
comment
Я вообще не верю, что ваши проблемы вызваны фрагментацией...   -  person Pubby    schedule 04.07.2016
comment
Если не фрагментация, то что?   -  person Danny    schedule 04.07.2016
comment
это на окнах в отладочной сборке?   -  person Richard Hodges    schedule 04.07.2016
comment
@ Дэнни, я оставил тебе ответ на твой предыдущий вопрос.   -  person Pubby    schedule 05.07.2016
comment
Centos 6 с ядром 3.10. Скомпилировано с -g   -  person Danny    schedule 05.07.2016


Ответы (1)


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

vector и deque принадлежат к первому семейству, list, set и map — ко второму.

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

Один из способов избежать этой проблемы - использовать vectors, используя "reserve", чтобы предвидеть потребность в памяти для уменьшения перемещений и сохранять сортировку данных при вставке.

Другой способ - использовать «контейнер на основе ссылок» (например, список, набор и т. Д.), Предоставляя им распределитель, который выделяет память из больших кусков, перерабатывая их вместо вызова необработанного malloc/free для вставки/удаления каждого отдельного элемента.

Взгляните на std::allocator.

Вы можете легко написать распределитель, производный от std::allocator и переопределив функции allocate/deallocate, добавив всю необходимую логику и передав yourallocator в качестве необязательного параметра шаблона контейнера, который вы хотите использовать.

person Emilio Garavaglia    schedule 04.07.2016
comment
Согласен с предложением пользовательского распределителя. - person Alan; 04.07.2016
comment
deque не размещает все свои элементы вместе. - person Nicol Bolas; 04.07.2016
comment
@NicolBolas: да, это что-то на полпути. Но лучше, чем список или набор. Я поставил его вместе с вектором, потому что он не выделяет по чистой базе 1/1. - person Emilio Garavaglia; 04.07.2016
comment
Спасибо за предложения. Что вы имеете в виду контейнеры, не поддерживающие глобальное перемещение? Гугл ничего не выдает по этому термину. Какие контейнеры поддерживают это? Я провожу больше тестов, но подозреваю, что проблема в std::map‹int, struct› fileInfoMap... - person Danny; 05.07.2016
comment
@Danny: все элементы вектора расположены в одном блоке памяти. Если оказывается, что он недостаточно широк, выделяется другой более широкий блок, элементы перемещаются, а старый блок памяти освобождается. Список, карта, набор и т. д. основаны на узлах, связанных указателями (формирующих двойные рукописные списки или B-деревья B&R), каждый из которых содержит один элемент (или пару ключ-значение), выделяемый индивидуально. Задача заставить эти узлы покинуть один и тот же блок не выполняется контейнером и не выполняется также стандартным std::allocator (который выполняет только malloc/free отдельных узлов) - person Emilio Garavaglia; 05.07.2016