Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added simple stats tracking memory usage #241

Merged
merged 7 commits into from
Aug 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions src/mem/largealloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,17 @@ namespace snmalloc
*/
AddressSpaceManager<PAL> address_space = {};

/**
* High-water mark of used memory.
*/
std::atomic<size_t> peak_memory_used_bytes{0};

public:
/**
* Memory current available in large_stacks
*/
std::atomic<size_t> available_large_chunks_in_bytes{0};

/**
* Stack of large allocations that have been returned for reuse.
*/
Expand Down Expand Up @@ -184,20 +194,34 @@ namespace snmalloc
{
// Cache line align
size_t size = bits::align_up(sizeof(T), 64);
size = bits::max(size, alignment);
void* p = address_space.template reserve<true>(bits::next_pow2(size));
size = bits::next_pow2(bits::max(size, alignment));
void* p = address_space.template reserve<true>(size);
if (p == nullptr)
return nullptr;

peak_memory_used_bytes += size;

return new (p) T(std::forward<Args...>(args)...);
}

template<bool committed>
void* reserve(size_t large_class) noexcept
{
size_t size = bits::one_at_bit(SUPERSLAB_BITS) << large_class;

peak_memory_used_bytes += size;
return address_space.template reserve<committed>(size);
}

/**
* Returns a pair of current memory usage and peak memory usage.
* Both statistics are very coarse-grained.
*/
std::pair<size_t, size_t> memory_usage()
{
size_t avail = available_large_chunks_in_bytes;
size_t peak = peak_memory_used_bytes;
return {peak - avail, peak};
}
};

using Stats = AllocStats<NUM_SIZECLASSES, NUM_LARGE_CLASSES>;
Expand Down Expand Up @@ -239,6 +263,7 @@ namespace snmalloc
else
{
stats.superslab_pop();
memory_provider.available_large_chunks_in_bytes -= rsize;

// Cross-reference alloc.h's large_dealloc decommitment condition.
bool decommitted =
Expand Down Expand Up @@ -284,18 +309,19 @@ namespace snmalloc
"without low memory notifications");
}

size_t rsize = bits::one_at_bit(SUPERSLAB_BITS) << large_class;

// Cross-reference largealloc's alloc() decommitted condition.
if (
(decommit_strategy != DecommitNone) &&
(large_class != 0 || decommit_strategy == DecommitSuper))
{
size_t rsize = bits::one_at_bit(SUPERSLAB_BITS) << large_class;

memory_provider.notify_not_using(
pointer_offset(p, OS_PAGE_SIZE), rsize - OS_PAGE_SIZE);
}

stats.superslab_push();
memory_provider.available_large_chunks_in_bytes += rsize;
memory_provider.large_stack[large_class].push(static_cast<Largeslab*>(p));
}
};
Expand Down
12 changes: 12 additions & 0 deletions src/override/malloc-extensions.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "malloc-extensions.h"

#include "../snmalloc.h"

using namespace snmalloc;

void get_malloc_info_v1(malloc_info_v1* stats)
{
auto next_memory_usage = default_memory_provider().memory_usage();
stats->current_memory_usage = next_memory_usage.first;
stats->peak_memory_usage = next_memory_usage.second;
}
34 changes: 34 additions & 0 deletions src/override/malloc-extensions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Malloc extensions
*
* This file contains additional non-standard API surface for snmalloc.
* The API is subject to changes, but will be clearly noted in release
* notes.
*/

/**
* Structure for returning memory used by snmalloc.
*
* The statistics are very coarse grained as they only track
* usage at the superslab/chunk level. Meta-data and object
* data is not tracked independantly.
*/
struct malloc_info_v1
{
/**
* Current memory usage of the allocator. Extremely coarse
* grained for efficient calculation.
*/
size_t current_memory_usage;

/**
* High-water mark of current_memory_usage.
*/
size_t peak_memory_usage;
};

/**
* Populates a malloc_info_v1 structure for the latest values
* from snmalloc.
*/
void get_malloc_info_v1(malloc_info_v1* stats);
103 changes: 103 additions & 0 deletions src/test/func/memory_usage/memory_usage.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Memory usage test
* Query memory usage repeatedly
*/

#include <iostream>
#include <test/setup.h>
#include <vector>

#define SNMALLOC_NAME_MANGLE(a) our_##a
#include "../../../override/malloc-extensions.cc"
#include "../../../override/malloc.cc"

using namespace snmalloc;

bool print_memory_usage()
{
static malloc_info_v1 last_memory_usage;
malloc_info_v1 next_memory_usage;

get_malloc_info_v1(&next_memory_usage);

if (
(next_memory_usage.current_memory_usage !=
last_memory_usage.current_memory_usage) ||
(next_memory_usage.peak_memory_usage !=
last_memory_usage.peak_memory_usage))
{
std::cout << "Memory Usages Changed to ("
<< next_memory_usage.current_memory_usage << ", "
<< next_memory_usage.peak_memory_usage << ")" << std::endl;
last_memory_usage = next_memory_usage;
return true;
}
return false;
}

std::vector<void*> allocs;

/**
* Add allocs until the statistics have changed n times.
*/
void add_n_allocs(size_t n)
{
while (true)
{
allocs.push_back(our_malloc(1024));
if (print_memory_usage())
{
n--;
if (n == 0)
break;
}
}
}

/**
* Remove allocs until the statistics have changed n times.
*/
void remove_n_allocs(size_t n)
{
while (true)
{
our_free(allocs.back());
allocs.pop_back();
if (print_memory_usage())
{
n--;
if (n == 0)
break;
}
}
}

int main(int argc, char** argv)
{
UNUSED(argc);
UNUSED(argv);

setup();

add_n_allocs(5);
std::cout << "Init complete!" << std::endl;

for (int i = 0; i < 10; i++)
{
remove_n_allocs(1);
std::cout << "Phase " << i << " remove complete!" << std::endl;
add_n_allocs(2);
std::cout << "Phase " << i << " add complete!" << std::endl;
}

for (int i = 0; i < 10; i++)
{
remove_n_allocs(2);
std::cout << "Phase " << i << " remove complete!" << std::endl;
add_n_allocs(1);
std::cout << "Phase " << i << " add complete!" << std::endl;
}

remove_n_allocs(3);
std::cout << "Teardown complete!" << std::endl;
}