Understanding the metrics

MALT exposes many metrics over its profiles, all the metrics will be listed and explained here.

Reminder on Memory

MALT as it is named (MAlloc Tracker) aimed at tracking all the malloc() of an application to report the memory used by itself.

For a C, C++, Rust, Fortran application there is 4 ways to request memory to the system from the language semantic.

You can find the general view here :

The 4 memory consumption parts.

The 4 memory consumption parts.

Global variables

The first common way is the global variables, they are allocated globaly for the run when spawning the process. Except if it contains specific values at start, a global variable is initially a virtual space allowed to be accessed in the memory and filled by default with zeroes.

Note that is you do not access it it will use virtual memory but not physical one You will find in MALT the ratio between those two consumption about global variables.

The amount of virtual memory is fixed at the start of the app, but the physical memory will increase over the run as we access the variable making it mapped in memory.

This is why you can see in the time charts in increase of the physicial memory without making any calls to mallocs.

Thread Local Storage (TLS)

The global variables are shared between all threads. In C, C++ there is a way to use an annotation to make a global variable private to each thread. This is called a Thread Local Storage says in acronym, a TLS. In this case, the memory consumption of the variable will be the size of the varaible times the number of threads.

Stacks

The local variables inside functions are stored into the stacks which can grow as we go deeper in the call hierarchy mainly when we do recursion. MALT is currently unperfect to track this memory but tries to give some hints if you compiled you code with -finstrument-functions on GCC compiler.

mmap

This is a call you generally never use at an app programer level. mmap is the main function to request memory to the operating system. It by default provides a segment which is virtually mapped. Meaning that is consumes virtual memory but not physical memory. By default on a first access, (first touch we say) a physical page will be filled with zeroes and mapped into the segment.

Notice here that the zeroes filling can imply a large overhead when the segment is large. This is way we should keep large allocations for a long time and not allocate / deallocate them intensively otherwise we can pass half of our time to just write zeroes into the memory.

Note that the cost of the zeroing will not be paied at the allocation time but latter in the computation at the first access to the considered memory region.

As a by design restriction, mapp only allow managing sizes and addresses multiple of then hardware page size generally 4 KB. This is way it is rarely used directly the application codes.

Remark that MALT will account the mmap calls only if they are done by the user, it will not count the once made by the memory allocator itself.

malloc

As mmap do not allow to handle any kind of sizes, malloc() has been built to fill this gap and by itself call mmap and handle the splitting of the segments it handles to store the allocations you request about any kind of sizes.

This is in C what is used to allocate dynamic memory and is the main function tracked by MALT.

Metrics

Here a short summary of the matrics exported by MALT :

Table of metrics

Metric

Short description

Allocated mem

Sum of all the mallocs and mmap done.

Allocated count

Count of all calls to malloc() and mmap()

Min. alloc size

Minimum block size allocated at this location.

Max. alloc size

Maximum block size allocated at this location.

Freed mem.

Amount of memory freed on this line.

Free count.

Number of call to free() function on this line.

Memory ops.

Total number of memory ops done on this line (malloc + mmap + free).

Local peak

Peak for this local line.

Global peak

Contribution to the peak of this specific line.

Leaks

Amount of memory leaking on this line.

Max lifetime

Maximum lifetime of the blocks on this line.

Min lifetime

Minimum lifetime on this line.

Recycling ratio

Number of time this line reallocated its peak memory.

Realloc count

Number of calls to realloc().

Realloc sum

Amount of memory allocated in total by realloc().

Mmap mem.

Amount of memory allocated in total by mmap().

Mmap count

Number of calls to mmap.

Munmap mem.

Amount of memory freed in total by munmap().

Munmap count

Number of calls to munmap.

Allocated memory

This metric just sum up the sizes given to all mallocs and mmap. In other words it represent the memory volume to be managed by the allocator and the system.

This metric give an indirect view on the time which is require to mange this volume of memory. Mainly if it becomes gigatbytes, it means this the code might take a long time to exchange its memory with the operating system.

Lets take an example:

void func() {
    void * a = malloc(32);          //allocated mem. of func = 32
    void * b = malloc(32);          //allocated mem. of func = 64
    free(b);
    void * c = malloc(64);          //allocated mem. of func = 128
    free(c);
    free(a);
    void * d = mmap(... 4096 ...);  //allocated mem. of func = 128 + 4096
    munmap(d...);
}

main() {
    func();                         //allocated mem. = 128 + 4096
}

Allocated count

The numer of memory allocation for the given line. Accounting the malloc and mmap. It show the potential oberhead due to malloc itself.

void func() {
    void * a = malloc(32);          //allocated count of func  = 1
    void * b = malloc(32);          //allocated count of func  = 2
    free(b);
    void * c = malloc(64);          //allocated count of func  = 3
    free(c);
    free(a);
    void * d = mmap(... 4096 ...);  //allocated count of func  = 4
    munmap(d...);
}

main() {
    func();                         //allocated count = 4
}

Min. alloc size

The minimum size passed as parameter of malloc or mmap on the given site. It permits to find the small memory allocations.

void func() {
    void * a = malloc(32);          //min. alloc size of func = 32
    void * b = malloc(32);          //min. alloc size of func = 32
    free(b);
    void * c = malloc(16);          //min. alloc size of func = 16
    free(c);
    free(a);
    void * d = mmap(... 4096 ...);  //min. alloc size of func = 16
    munmap(d...);
}

int main() {
    func();                         //min. alloc size = 16
}

Max. alloc size

Maximum size passed to malloc or mmap on the given line. It permits to find the large memory allocations in the application.

void func() {
    void * a = malloc(32);          //max. alloc size of func = 32
    void * b = malloc(32);          //max. alloc size of func = 32
    free(b);
    void * c = malloc(16);          //max. alloc size of func = 32
    free(c);
    free(a);
    void * d = mmap(... 4096 ...);  //max. alloc size of func = 4096
    munmap(d...);
}

int main() {
    func();                         //max. alloc size = 4096
}

Freed mem.

Amount of memory freed on the given line by free or munmap. It is computed by summing the size of all the blocks freed on the given location.

void func() {
    void * a = malloc(32);
    void * b = malloc(32);
    free(b);                        //freed mem. of func = 32
    void * c = malloc(16);
    free(c);                        //freed mem. of func = 48
    free(a);                        //freed mem. of func = 80
    void * d = mmap(... 4096 ...);
    munmap(d...);                   //freed mem. of func = 80 + 4096
}

int main() {
    func();                         //freed count. = 80 + 4096
}

Free count

Number of calls to free and munmap functions.

void func() {
    void * a = malloc(32);
    void * b = malloc(32);
    free(b);                        //freed count. of func = 1
    void * c = malloc(16);
    free(c);                        //freed count. of func = 2
    free(a);                        //freed count. of func = 3
    void * d = mmap(... 4096 ...);
    munmap(d...);                   //freed count. of func = 4
}

int main() {
    func();                         //freed count. = 4
}

Memory ops

Count the number of memory operations done on a given location.

void func() {
    void * a = malloc(32);          // memory ops. of func = 1
    void * b = malloc(32);          // memory ops. of func = 2
    free(b);                        // memory ops. of func = 3
    void * c = malloc(16);          // memory ops. of func = 4
    free(c);                        // memory ops. of func = 5
    free(a);                        // memory ops. of func = 6
    void * d = mmap(... 4096 ...);  // memory ops. of func = 7
    munmap(d...);                   // memory ops. of func = 8
}

int main() {
    func();                         // memory ops. of func = 8
}

Local peak

This is the memory consumption peak for a given code location. It takes in account all the memory allocation done on this particular location.

void func() {
    void * a = malloc(32);          // local peak of func = 32
    void * b = malloc(32);          // local peak of func = 64
    free(b);
    void * c = malloc(16);          // local peak of func = 64
    free(c);
    void * d = mmap(... 4096 ...);  // local peak of func = 32 + 4096
    munmap(d...);
}

int main() {
    void * buffer = malloc(256);
    func();                         // local peak = 48 + 4096
}

Global peak

This metrics gives the memory consumption of the local site at the peak time.

Notice it can arise at a different moment than the local peak because the global peak does not necesserily arise at the time the local peak arise.

Leaks

This is the mallocs which are not paired with a free or an mmap not paried or partially paired with an munmap.

void func() {
    void * a = malloc(32);          // leak = 32
    void * b = malloc(32);
    free(b);
    void * c = malloc(16);
    free(c);
    void * d = mmap(... 4096 ...);
    munmap(d...);
}

int main() {
    void * buffer = malloc(256);
    func();                         // leak = 32
}

Max lifetime

It takes the maximal delay between a malloc and its paired free on the given location.

If this value is too low and there is a large amount of allocation done at the given location it means that there is possibly a performance issue.

Min lifetime

It takes the minimal delay between a malloc and its paired free on the given location.

Recycling ratio

It compute the ratio of the allocatd volume over the local peak. It give a rougth estimation of the number of time the function reallocated its peak memory.

void func() {
    void * a = malloc(32);          // recyclong ratio = 1
    free(a);
    for (size_t i = 0 ; i < 1000 ; i++) {
        void * c = malloc(16);      // recyclong ratio = 1000
        free(c);
    }
}

int main() {
    void * buffer = malloc(256);
    func();                         // recyclong ratio = 501 ((32 + 16*1000) / 32)
}

Realloc count

Count the number of realloc done at this location.

void func() {
    void * a = realloc(nullptr, 32);       // realloc count of func = 1
    a = realloc(a, 64);                    // realloc count of func = 2
    a = realloc(a, 16);                    // realloc count of func = 3
    free(a);
}

int main() {
    func();                                // realloc count of func = 3
}

Realloc sum

Sum the memory delta of each realloc done at the given location.

void func() {
    void * b = realloc(nullptr, 10);       // realloc sum of func = 10
    void * a = realloc(nullptr, 32);       // realloc sum of func = 42
    a = realloc(a, 64);                    // realloc sum of func = 74
    a = realloc(a, 64);                    // realloc sum of func = 74
    a = realloc(a, 16);                    // realloc sum of func = 26
    free(a);
    free(b);
}

int main() {
    func();                                // realloc sum of func = 26
}

Mmap mem

Sum the memory allocated by each mmap done at the given location.

void func() {
    void * a = mmap(.... 1024*1024 ....);       // mmap mem of func = 1 MB
    void * a = mremap(a, .... 2048*1024 ....);  // mmap mem of func = 2 MB
    munmap(a + 1024*1024, 1024*1024);           // mmap mem of func = 2 MB
    void * a = mremap(a, .... 2048*1024 ....);  // mmap mem of func = 3 MB
    munmap(a, 2048*1024);
}

int main() {
    func();                                     // mmap mem = 3 MB
}

Mmap count

Count the number of mmap done at the given location.

void func() {
    void * a = mmap(.... 1024*1024 ....);       // mmap count of func = 1
    void * a = mremap(a, .... 2048*1024 ....);  // mmap count of func = 2
    munmap(a + 1024*1024, 1024*1024);
    void * a = mremap(a, .... 2048*1024 ....);  // mmap count of func = 3
    munmap(a, 2048*1024);
}

int main() {
    func();                                     // mmap count = 3
}

Munmap mem

Sum the memory freed by each munmap done at the given location.

void func() {
    void * a = mmap(.... 1024*1024 ....);
    void * a = mremap(a, .... 2048*1024 ....);
    munmap(a + 1024*1024, 1024*1024);           // munmap mem of func = 1 MB
    void * a = mremap(a, .... 2048*1024 ....);
    munmap(a, 2048*1024);                       // munmap mem of func = 3 MB
}

int main() {
    func();                                    // munmap mem = 3 MB
}

Munmap count

Count the number of munmap done at the given location.

void func() {
    void * a = mmap(.... 1024*1024 ....);
    void * a = mremap(a, .... 2048*1024 ....);
    munmap(a + 1024*1024, 1024*1024);           // munmap count of func = 1
    void * a = mremap(a, .... 2048*1024 ....);
    munmap(a, 2048*1024);                       // munmap count of func = 2
}

int main() {
    func();                                    // munmap count = 2
}