Как утечка памяти улучшает производительность - PullRequest
4 голосов
/ 04 августа 2010

Я строю большое RTree (пространственный индекс), полное узлов. Он должен уметь обрабатывать множество запросов и обновлений. Объекты постоянно создаются и уничтожаются. Основной тест, который я запускаю, состоит в том, чтобы увидеть производительность дерева по мере увеличения количества объектов в дереве. Я вставляю из 100-20000 случайно расположенных объектов одинакового размера с шагом 100. Поиск и обновление не имеют отношения к проблеме, с которой я сталкиваюсь в настоящее время.

Теперь, когда NO утечка памяти, производительность "вставки в дерево" повсюду. Он идет от 10,5 секунд с ~ 15000 объектов до 1,5 с ~ 18000. Там нет никакого шаблона вообще.

Когда я намеренно добавляю утечку, так же просто, как ввод "new int;" Я ни к чему не приписываю, прямо там, где есть линия, производительность мгновенно падает на красивую плавную кривую, наклоняющуюся от 0 (примерно) секунд для 100 объектов до 1,5 для полных 20k.

Очень, очень потерян в этот момент. Если вы хотите исходный код, я могу включить его, но это huuugggeeee и буквально единственная строка, которая имеет значение, это "new int;"

Заранее спасибо! -nick

Ответы [ 5 ]

2 голосов
/ 04 августа 2010

Без дополнительной информации невозможно быть уверенным.

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

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

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

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

2 голосов
/ 04 августа 2010

Я не уверен, как вы пришли к этому new int тесту, но это не очень хороший способ исправить все :) Запустите ваш код с помощью профилировщика и выясните, где реальные задержки. Затем сконцентрируйтесь на исправлении горячих точек.

g ++ имеет встроенную функцию - просто скомпилируйте с -pg

0 голосов
/ 06 августа 2010

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

Когда кто-то выделяет память в куче, ячейка нужного размера отрывается от кучи.Когда память освобождается, ячейка добавляется в список freelist.Когда кто-то делает новое выделение, распределитель обходит кучу, пока не будет найдена достаточно большая ячейка.При выполнении большого количества распределений свободный список может быть довольно длинным, а просмотр списка может занять нетривиальное время.

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

Однако есть вероятность, что вы выделяете и освобождаете объекты аналогичного размера.Если вы используете свои собственные списки фрилансеров, вы обезопасите себя от многочисленных прогулок в куче и сможете значительно повысить производительность.Это именно то, что делают распределители STL для улучшения производительности.

0 голосов
/ 04 августа 2010

Решение: не запускать из Visual Studio.На самом деле запустить файл .exe.Понял это, потому что это то, что делали профилировщики, и цифры магически падали.Проверенное использование памяти и запуск версии (и предоставление мне ИСКЛЮЧИТЕЛЬНОГО времени) не приводило к чрезмерно огромным размерам.

Решение, почему, черт возьми, Visual Studio делает глупую чушь, как это: Никакой подсказки.

0 голосов
/ 04 августа 2010

Возможная вещь, которая происходит, которая объясняет это (теория)

  1. Компилятор не удалил пустой новый int
  2. Новый int находится в одном из внутренних циклов или где-то в вашем рекурсивном обходе, где он выполняется больше всего времени
  3. Общая RSS процесса увеличивается и в конечном итоге общая память используется процессом
  4. Из-за этого возникают ошибки страницы
  5. Из-за сбоев страниц процесс становится связанным с вводом / выводом, а не с процессором Конечный результат, вы видите падение пропускной способности. Будет полезно, если вы упомянете используемый компилятор и параметры компилятора, который вы используете для сборки кода.
...