Как измерять использование памяти std:: unordered_map

Мы знаем, что реализация контейнеров на основе хэш-таблиц, таких как std::unordered_map, использует много памяти, но я не знаю, сколько стоит?

Помимо обозначений пространственной сложности и не учитывая, является ли контейнерный элемент указателем на более крупный объект:

Есть ли способ выяснить, сколько байтов используется таким контейнером во время выполнения?

Можно ли во время выполнения указать, сколько памяти использует какой-либо контейнер?

Ответ 1

Если вы хотите получить приблизительный размер, я думаю, что bucket_count() и max_load_factor() достаточно, что дает текущее количество ведер и коэффициент загрузки.

Rational:

  • Если load_factor <= 1, это означает, что bucket_count() >= элементы на карте, тогда bucket_count() - это размер использования памяти.

  • Если load_factor > 1, то bucket_count() * load_factor указывает максимальный элемент на карте. Обратите внимание, что это максимальный размер, а не реальный размер.

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

  unsigned n = mymap.bucket_count();
  float m = mymap.max_load_factor();
  if (m > 1.0) {
    return n * m;
  }
  else {
    return n;
  }

Если вы хотите получить точное использование памяти, вам может понадобиться подсчитать все ведра, чтобы увидеть, сколько элементов в нем:

  size_t count = 0;
  for (unsigned i = 0; i < mymap.bucket_count(); ++i) {
    size_t bucket_size = mymap.bucket_size(i);
    if (bucket_size == 0) {
      count++;
    }
    else {
      count += bucket_size;
    }
  }

Ответ 2

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

  • size() указывает, сколько элементов данных было вставлено в контейнер
  • bucket_count() указывает, сколько ведер содержит базовую хеш-таблицу, каждый из которых может ожидать размещения связанного списка с связанными элементами.

Сейчас:

  • байты, фактически используемые для хранения элементов, будут m.size() * sizeof(M::value_type)

  • байты, используемые для ведрах хэш-таблицы, зависят от способа хранения внутренних списков - std::unordered_map::bucket_size имеет постоянную сложность, поэтому мы можем разумно предположить, что будет указатель size() и указатель головы на каждый ковш, поэтому m.bucket_count() * (sizeof(size_t) + sizeof(void*)) является разумной догадкой, хотя может быть, что только постоянная амортизированная сложность из-за ограниченности load_factor() и отсутствия size для каждого ведра (я бы предпочел реализовать ее таким образом самостоятельно)

  • если каждый из вставленных элементов является частью списка, ему понадобится указатель next, поэтому мы можем добавить еще один m.size() * sizeof(void*)

  • каждое распределение памяти может быть округлено до размера, удобного для управления библиотекой распределения памяти. следующая мощность в два, что приближается к 100% наихудшему случаю неэффективности и 50% в среднем, поэтому пусть добавляет 50%, только для узлов списка, так как ведра, вероятно, обладают двумя заданными size_t и указателем: 50% * size() * (sizeof(void*) + sizeof((M::value_type))

  • особенно в режиме отладки, может быть любое количество данных, связанных с реализацией и обнаружением ошибок

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