Измерение потребления памяти, используемого параллельным кодом на C++

Я реализую классы FineList и LazyList на C++. Оба приведенных выше параллельных связанных списка были реализованы на Java в книге "Искусство многопроцессорного программирования". Я хотел бы измерить объем памяти, потребляемый каждым алгоритмом во время всего его выполнения. Я не уверен, как мне это сделать. Я мог бы просто отслеживать количество вызовов "new" каждым потоком в счетчике в обоих алгоритмах и использовать это в качестве критерия измерения. Точно так же всякий раз, когда поток вызывает «delete», я уменьшаю значение счетчика. Является ли это справедливым критерием для измерения потребления памяти? Проблема в том, что алгоритм FineList позволяет мне немедленно «удалить» узел после его удаления из связанного списка из-за его природы, основанной на блокировке. Но это не относится к алгоритму LazyList, так как он имеет методы без блокировки. Есть ли другой способ измерить потребление памяти или вышеуказанный метод справедлив для обоих алгоритмов?


person Dee Jay    schedule 19.06.2018    source источник
comment
Какую операционную систему/среду вы используете? Для этого есть инструменты   -  person Dominic Price    schedule 19.06.2018
comment
Я использую Убунту 14.04   -  person Dee Jay    schedule 19.06.2018
comment
Обратите внимание, что количество вызовов new и delete не обязательно коррелирует с объемом выделенной памяти (хотя и указывает на накладные расходы на отток/выделение).   -  person Cameron    schedule 19.06.2018
comment
getrusage() может быть тем, что вы ищете, вы можете вызывать его в различных точках вашей функции. См. stackoverflow.com/questions/63166/ например   -  person Dominic Price    schedule 19.06.2018
comment
@DominicPrice, если я использую getrusage() в конце выполнения алгоритма, покажет ли он фактическое потребление памяти для всего выполнения?   -  person Dee Jay    schedule 19.06.2018
comment
@Cameron, поскольку я реализую его на C ++ и у него нет сборщика мусора, разве вызовы не покажут точное количество использования памяти каждым алгоритмом?   -  person Dee Jay    schedule 19.06.2018
comment
@DeeJay: Да, если вы также отслеживаете размер ассигнований. По какой-то причине я подумал, что вы имеете в виду подсчет только количества вызовов new/delete :-) Однако имейте в виду, что во многих кодах используется базовый malloc напрямую.   -  person Cameron    schedule 20.06.2018


Ответы (2)


Если вы ведете упорядоченный журнал вызовов new и delete (включая запрошенный размер) и уверены, что интересующий вас код использует только new и delete, а не другие процедуры распределения, вы можете определить более или менее теоретическое потребление памяти в любой момент времени путем ведения текущего подсчета избытка выделенной памяти по сравнению с освобожденной памятью. Возможно, вы могли бы создать такой журнал, перегрузив глобальный operator new(size_t), а также удалить.

Число будет только теоретическим из-за нескольких факторов:

  1. Распределители добавляют определенные накладные расходы, поэтому фактическая выделенная память, как правило, будет больше, чем сумма размеров выделенных объектов. Эти накладные расходы включают фрагментацию, поскольку часть нераспределенной памяти может быть технически свободной, но недоступной на самом деле.
  2. Распределители могут не возвращать никакой памяти ОС или могут возвращать ее, но непредсказуемым образом. Поэтому, если вы измеряете «выделенную память» для ОС (по сравнению с распределителем времени выполнения), у вас более сложная проблема, и это сильно зависит от распределителя.
  3. Особенно в многопоточном сценарии не вся освобожденная память обязательно используется будущими выделениями. Конкретным случаем этого для аллокаторов с поддержкой потоков является использование буферов распределения локальных потоков: память, освобожденная в одном потоке, не может быть немедленно использована в другом потоке, пока не будет достигнут некоторый порог, при котором выделения могут перемещаться между потоками. Это может иметь значение для вашего сценария, если между потоками существует несоответствие узлов, выделенных и освобожденных.
  4. Существует целый уровень сложности, когда дело доходит до определения того, сколько памяти было выделено — даже для определения этого термина. Например, для большого распределения ОС может вернуть вам кусок памяти, которого на самом деле нет в ОЗУ, и только страница, которая лениво отображается при доступе к ней. Поэтому, если вы не получаете доступ ко всему, что вы выделяете, цифры, сообщаемые распределителем, могут быть завышенными.

Это просто царапины на поверхности.

person BeeOnRope    schedule 19.06.2018

C++ позволяет вам предоставлять свои собственные operator new и соответствующие operator delete. Это полезно для таких измерительных задач. Вы даже можете использовать это, чтобы выяснить, какая память используется в зависимости от стратегии распределения, например. посмотрите, сколько памяти требуется вашему алгоритму при округлении выделения до кратного 16 байтам.

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

Имейте в виду, идея программирования без блокировок может быть чрезмерно оптимистичной, если вам действительно нужен new. В С++ нет никакой гарантии, что new не блокируется. А поскольку C++ позволяет вам new памяти в одном потоке и delete в другом, необходима некоторая синхронизация между потоками.

person MSalters    schedule 20.06.2018