У меня есть программа, которая анализирует 150000 файлов. Valgrind сообщает об отсутствии утечки памяти, но программа со временем замедляется.
Некоторые проблемы были связаны с слишком частым использованием std :: string и mktime, который занимал слишком много времени. (увидеть C ++ замедляет чтение 70000 файлов)
Но это все еще замедляется со временем. 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
— список уникальных строк, содержащих 150000 путей.
Я выбрал std :: set, потому что он автоматически обрабатывает дубликаты и автоматически поддерживает порядок сортировки.
Нет произвольного доступа, только добавлять записи, сортировать их (вручную или автоматически) и перебирать их.
fileInfoMap
— массив структур с ключом time_t timestamp, соответствующим дате файла.
Я выбрал std :: map. Он также будет иметь 150 000 записей, поэтому занимает много памяти.
Нет произвольного доступа, только добавить записи на одном конце. Нужно перебрать их и, если необходимо, удалить записи из середины.
eventList
— небольшой список «событийных» структур, скажем, 50 наименований.
Я выбрал std :: vector. Не уверен, почему на самом деле.
Нет произвольного доступа, только добавить записи в один конец, а затем итерации по коллекции.
Я довольно новичок в C ++. Спасибо за внимание.
Что касается управления памятью, контейнер принадлежит двум большим семействам: одно, которое выделяет все элементы вместе, и другое, которое выделяет элементы отдельно.
vector и deque принадлежат первому семейству, списку, множеству и сопоставляются со вторым.
Фрагментация памяти возникает, когда элементы постоянно добавляются и удаляются из контейнера, который не поддерживает глобальное перемещение.
Одним из способов избежать этой проблемы является использование vector
s, используя «reserve
«Чтобы предвидеть память, необходимо уменьшить перемещение и сохранить данные отсортированными при вставке.
Другой способ — использовать «контейнер, основанный на связывании» (например, список, набор и т. Д.), Предоставляя им распределитель, который выделяет память из больших кусков, перерабатывая их вместо вызова необработанного malloc / free для каждого отдельного элемента вставки / удаления.
Посмотри на станд :: распределитель
Вы можете легко написать распределитель, производя от std :: allocator и переопределяя allocate
/deallocate
функции, добавляющие всю необходимую логику и передавая yourallocator
в качестве необязательного параметра шаблона контейнера, который вы хотите использовать.
Других решений пока нет …