#c
#c
Вопрос:
Я хочу знать программный способ получить память, потребляемую моим пользовательским классом. Ниже приводится объявление класса
struct TrieNode {
typedef std::map<char, TrieNode *> ChildType;
std::string m_word;
bool m_visited;
}
Я вставил в это около 264061
слов Trie
. После этого, когда я это делаю, sizeof(trieobject)
это просто покажите мне 32
. Откуда мне знать, сколько именно памяти используется такими структурами данных.
Комментарии:
1. Вам нужно будет отслеживать динамические распределения. Если вы просто настроите простую тестовую программу, вы можете использовать
valgrind
для отчета об использовании кучи. В противном случае вам нужно будет изменить распределители, чтобы заставить их выводить статистику.2. Constantinius, это дерево, возможно, слова находятся в дочернем элементе дочернего элемента.
3. Это реальный код?
TrieNode
В коде нет дочерних элементов, он определяет только тип дочерних элементов и имеет два члена типов:std::string
иbool
4. sizeof(std::string) sizeof(bool) m_word.capacity() байты заполнения или sizeof (trieobject) m_word.capacity()
Ответ №1:
Я использую
valgrind --tool=massif ./myprogram -opt arg1 arg2
ms_print massif.* | less -SR
для этого. Пример вывода с этой страницы
19.63^ ###
| #
| # ::
| # : :::
| :::::::::# : : ::
| : # : : : ::
| : # : : : : :::
| : # : : : : : ::
| ::::::::::: # : : : : : : :::
| : : # : : : : : : : ::
| ::::: : # : : : : : : : : ::
| @@@: : : # : : : : : : : : : @
| ::@ : : : # : : : : : : : : : @
| :::: @ : : : # : : : : : : : : : @
| ::: : @ : : : # : : : : : : : : : @
| ::: : : @ : : : # : : : : : : : : : @
| :::: : : : @ : : : # : : : : : : : : : @
| ::: : : : : @ : : : # : : : : : : : : : @
| :::: : : : : : @ : : : # : : : : : : : : : @
| ::: : : : : : : @ : : : # : : : : : : : : : @
0 ----------------------------------------------------------------------->KB 0 29.48
Number of snapshots: 25
Detailed snapshots: [9, 14 (peak), 24]
В оставшейся части журнала указаны наивысшие процентили выделений памяти, вы можете конкретно увидеть, какой тип класса занимает какой% памяти кучи (и откуда исходят выделения в терминах стека вызовов), например:
--------------------------------------------------------------------------------
n time(B) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
10 10,080 10,080 10,000 80 0
11 12,088 12,088 12,000 88 0
12 16,096 16,096 16,000 96 0
13 20,104 20,104 20,000 104 0
14 20,104 20,104 20,000 104 0
99.48% (20,000B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->49.74% (10,000B) 0x804841A: main (example.c:20)
|
->39.79% (8,000B) 0x80483C2: g (example.c:5)
| ->19.90% (4,000B) 0x80483E2: f (example.c:11)
| | ->19.90% (4,000B) 0x8048431: main (example.c:23)
| |
| ->19.90% (4,000B) 0x8048436: main (example.c:25)
|
->09.95% (2,000B) 0x80483DA: f (example.c:10)
->09.95% (2,000B) 0x8048431: main (example.c:23)
Ответ №2:
Ну, это не так просто сделать. Прежде всего, m_word — это строка с переменным размером, верно? Внутри std::string, помимо прочего, содержит массив символов. То же самое означает std::map . Я думаю, вы могли бы получить приблизительную оценку, основанную на размере map * TrieNode, но это будет всего лишь приблизительная оценка.
Я думаю, что некоторое профилирование кода с помощью внешнего инструмента было бы более полезным. Черт возьми, вы даже можете использовать диспетчер задач, если у вас не осталось никаких инструментов :).
Ответ №3:
Ваш «размер объекта» равен sizeof(std::string) sizeof(bool) m_word.capacity() байты заполнения или sizeof (trieobject) m_word.capacity()
Ответ №4:
Вот фрагмент кода для GCC, который я придумал, который вы можете использовать в тестовой программе, где вы создаете экземпляр только одного объекта вашего класса и выполняете с ним некоторую типичную работу. Код заменяет глобальные operator new()
и operator delete()
; таким образом, он будет отслеживать распределения только с помощью ::new
выражений и стандартного распределителя, при условии, что сам стандартный распределитель использует ::operator new()
(это относится к GCC).
Поскольку нам нужно отслеживать указатели и их распределение, для этого нам нужна отдельная карта, которая, конечно, не может использовать сам стандартный распределитель; на помощь приходит GCC malloc-allocator.
Мы используем статически инициализированный глобальный файл, чтобы заставить средство отслеживания памяти печатать свои данные после main
возврата.
#include <unordered_map>
#include <string>
#include <iostream>
#include <ext/malloc_allocator.h>
struct Memtrack
{
typedef std::unordered_map<void*, std::size_t, std::hash<void*>,
std::equal_to<void*>, __gnu_cxx::malloc_allocator<void*>> AllocMap;
static int memtrack;
static int memmax;
static AllocMap allocs;
Memtrack() { std::cout << "starting tracker: cur = " << memtrack << ", max = " << memmax << ".n"; }
~Memtrack() { std::cout << "ending tracker: cur = " << memtrack << ", max = " << memmax << ".n"; }
static void track_new(std::size_t n, void * p)
{
memtrack = n;
if (memmax < memtrack) memmax = memtrack;
allocs[p] = n;
std::cout << "... allocating " << n << " bytes...n";
}
static void track_delete(void * p)
{
const int n = int(allocs[p]);
memtrack -= n;
std::cout << "... freeing " << n << " bytes...n";
}
} m;
int Memtrack::memtrack = 0;
int Memtrack::memmax = 0;
Memtrack::AllocMap Memtrack::allocs;
void * operator new(std::size_t n) throw(std::bad_alloc)
{
void * const p = std::malloc(n);
Memtrack::track_new(n, p);
return p;
}
void operator delete(void * p) throw()
{
Memtrack::track_delete(p);
std::free(p);
}
int main()
{
std::cout << "Beginning of main.n";
std::unordered_map<std::string, int> m; // this piece of code
m["hello"] = 4; // is a typical test for working
m["world"] = 7; // with dynamic allocations
std::cout << "End of main.n";
}
Некоторые типичные выходные данные:
starting tracker: cur = 0, max = 0.
Beginning of main.
... allocating 48 bytes...
... allocating 12 bytes...
... allocating 12 bytes...
End of main.
... freeing 12 bytes...
... freeing 12 bytes...
... freeing 48 bytes...
ending tracker: cur = 0, max = 72.
Ответ №5:
Тривиально. Если у вас есть некоторое время (что может иметь место, если вас интересует размер только в целях отладки / оптимизации). Этот подход может оказаться неподходящим для производственного кода!
#include <malloc.h>
template <typename T> int objSize(T const* obj) {
// instead of uordblks, you may be interested in 'arena', you decide!
int oldSize = mallinfo().uordblks;
T* dummy = new T(*obj);
int newSize = mallinfo().uordblks;
delete dummy;
return newSize - oldSize;
}
Комментарии:
1.
mallinfo
не является стандартным,<malloc.h>
является заголовком C иnew
не требуется использоватьmalloc()
под ним для фактического выделения памяти.2.@Karl Knechtel: Я не могу найти, где в OP указаны требования, которые вы подразумеваете в своем комментарии. Но, к вашему сведению: ну и что, что
malloc.h
это заголовок C, совсем не редкость включать заголовки C в C . Более того:malloc
недостаточно, так как вам нужно вызвать конструктор копирования (вот почему мне нужноnew
), чтобы он скопировал элементыT
. На самом деле, если быmalloc
было достаточно, вы бы знали требуемый размер априори, что сделало бы все упражнение бессмысленным, не так ли?