Major refactor of snmalloc (#343)
# Pagemap The Pagemap now stores all the meta-data for the object allocation. The meta-data in the pagemap is effectively a triple of the sizeclass, the remote allocator, and a pointer to a 64 byte block of meta-data for this chunk of memory. By storing the pointer to a block, it allows the pagemap to handle multiple slab sizes without branching on the fast path. There is one entry in the pagemap per 16KiB of address space, but by using the same entry in the pagemap for 4 adjacent entries, then we can treat a 64KiB range can be treated as a single slab of allocations. This change also means there is almost no capability amplification required by the implementation on CHERI for finding meta-data. The only amplification is required, when we change the way a chunk is used to a size of object allocation. # Backend There is a second major aspect of the refactor that there is now a narrow API that abstracts the Pagemap, PAL and address space management. This should better enable the compartmentalisation and makes it easier to produce alternative backends for various research directions. This is a template parameter that can be used to specialised by the front-end in different ways. # Thread local state The thread local state has been refactored into two components, one (called 'localalloc') that is stored directly in the TLS and is constant initialised, and one that is allocated in the address space (called 'coreallloc') which is lazily created and pooled. # Difference This removes Superslabs/Medium slabs as there meta-data is now part of the pagemap.
This commit is contained in:
committed by
GitHub
parent
18d7cc99b6
commit
f0e2ab702a
@@ -7,67 +7,35 @@
|
||||
|
||||
#include "test/setup.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <snmalloc.h>
|
||||
#include <thread>
|
||||
|
||||
/**
|
||||
* This test is checking lazy init is correctly done with `get`.
|
||||
*
|
||||
* The test is written so platforms that do not do lazy init can satify the
|
||||
* test.
|
||||
*/
|
||||
void get_test()
|
||||
{
|
||||
// This should get the GlobalPlaceHolder if using lazy init
|
||||
auto a1 = snmalloc::ThreadAlloc::get_noncachable();
|
||||
|
||||
// This should get a real allocator
|
||||
auto a2 = snmalloc::ThreadAlloc::get();
|
||||
|
||||
// Trigger potential lazy_init if `get` didn't (shouldn't happen).
|
||||
a2->dealloc(a2->alloc(5));
|
||||
|
||||
// Get an allocated allocator.
|
||||
auto a3 = snmalloc::ThreadAlloc::get_noncachable();
|
||||
|
||||
if (a1 != a3)
|
||||
{
|
||||
printf("Lazy test!\n");
|
||||
// If the allocators are different then lazy_init has occurred.
|
||||
// This should have been caused by the call to `get` rather than
|
||||
// the allocations.
|
||||
if (a2 != a3)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void alloc1(size_t size)
|
||||
{
|
||||
void* r = snmalloc::ThreadAlloc::get_noncachable()->alloc(size);
|
||||
snmalloc::ThreadAlloc::get_noncachable()->dealloc(r);
|
||||
void* r = snmalloc::ThreadAlloc::get().alloc(size);
|
||||
snmalloc::ThreadAlloc::get().dealloc(r);
|
||||
}
|
||||
|
||||
void alloc2(size_t size)
|
||||
{
|
||||
auto a = snmalloc::ThreadAlloc::get_noncachable();
|
||||
void* r = a->alloc(size);
|
||||
a->dealloc(r);
|
||||
auto& a = snmalloc::ThreadAlloc::get();
|
||||
void* r = a.alloc(size);
|
||||
a.dealloc(r);
|
||||
}
|
||||
|
||||
void alloc3(size_t size)
|
||||
{
|
||||
auto a = snmalloc::ThreadAlloc::get_noncachable();
|
||||
void* r = a->alloc(size);
|
||||
a->dealloc(r, size);
|
||||
auto& a = snmalloc::ThreadAlloc::get();
|
||||
void* r = a.alloc(size);
|
||||
a.dealloc(r, size);
|
||||
}
|
||||
|
||||
void alloc4(size_t size)
|
||||
{
|
||||
auto a = snmalloc::ThreadAlloc::get();
|
||||
void* r = a->alloc(size);
|
||||
a->dealloc(r);
|
||||
auto& a = snmalloc::ThreadAlloc::get();
|
||||
void* r = a.alloc(size);
|
||||
a.dealloc(r);
|
||||
}
|
||||
|
||||
void check_calloc(void* p, size_t size)
|
||||
@@ -77,7 +45,16 @@ void check_calloc(void* p, size_t size)
|
||||
for (size_t i = 0; i < size; i++)
|
||||
{
|
||||
if (((uint8_t*)p)[i] != 0)
|
||||
{
|
||||
std::cout << "Calloc contents:" << std::endl;
|
||||
for (size_t j = 0; j < size; j++)
|
||||
{
|
||||
std::cout << std::hex << (size_t)((uint8_t*)p)[j] << " ";
|
||||
if (j % 32 == 0)
|
||||
std::cout << std::endl;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
// ((uint8_t*)p)[i] = 0x5a;
|
||||
}
|
||||
}
|
||||
@@ -86,54 +63,53 @@ void check_calloc(void* p, size_t size)
|
||||
void calloc1(size_t size)
|
||||
{
|
||||
void* r =
|
||||
snmalloc::ThreadAlloc::get_noncachable()->alloc<snmalloc::ZeroMem::YesZero>(
|
||||
size);
|
||||
snmalloc::ThreadAlloc::get().alloc<snmalloc::ZeroMem::YesZero>(size);
|
||||
check_calloc(r, size);
|
||||
snmalloc::ThreadAlloc::get_noncachable()->dealloc(r);
|
||||
snmalloc::ThreadAlloc::get().dealloc(r);
|
||||
}
|
||||
|
||||
void calloc2(size_t size)
|
||||
{
|
||||
auto a = snmalloc::ThreadAlloc::get_noncachable();
|
||||
void* r = a->alloc<snmalloc::ZeroMem::YesZero>(size);
|
||||
auto& a = snmalloc::ThreadAlloc::get();
|
||||
void* r = a.alloc<snmalloc::ZeroMem::YesZero>(size);
|
||||
check_calloc(r, size);
|
||||
a->dealloc(r);
|
||||
a.dealloc(r);
|
||||
}
|
||||
|
||||
void calloc3(size_t size)
|
||||
{
|
||||
auto a = snmalloc::ThreadAlloc::get_noncachable();
|
||||
void* r = a->alloc<snmalloc::ZeroMem::YesZero>(size);
|
||||
auto& a = snmalloc::ThreadAlloc::get();
|
||||
void* r = a.alloc<snmalloc::ZeroMem::YesZero>(size);
|
||||
check_calloc(r, size);
|
||||
a->dealloc(r, size);
|
||||
a.dealloc(r, size);
|
||||
}
|
||||
|
||||
void calloc4(size_t size)
|
||||
{
|
||||
auto a = snmalloc::ThreadAlloc::get();
|
||||
void* r = a->alloc<snmalloc::ZeroMem::YesZero>(size);
|
||||
auto& a = snmalloc::ThreadAlloc::get();
|
||||
void* r = a.alloc<snmalloc::ZeroMem::YesZero>(size);
|
||||
check_calloc(r, size);
|
||||
a->dealloc(r);
|
||||
a.dealloc(r);
|
||||
}
|
||||
|
||||
void dealloc1(void* p, size_t)
|
||||
{
|
||||
snmalloc::ThreadAlloc::get_noncachable()->dealloc(p);
|
||||
snmalloc::ThreadAlloc::get().dealloc(p);
|
||||
}
|
||||
|
||||
void dealloc2(void* p, size_t size)
|
||||
{
|
||||
snmalloc::ThreadAlloc::get_noncachable()->dealloc(p, size);
|
||||
snmalloc::ThreadAlloc::get().dealloc(p, size);
|
||||
}
|
||||
|
||||
void dealloc3(void* p, size_t)
|
||||
{
|
||||
snmalloc::ThreadAlloc::get()->dealloc(p);
|
||||
snmalloc::ThreadAlloc::get().dealloc(p);
|
||||
}
|
||||
|
||||
void dealloc4(void* p, size_t size)
|
||||
{
|
||||
snmalloc::ThreadAlloc::get()->dealloc(p, size);
|
||||
snmalloc::ThreadAlloc::get().dealloc(p, size);
|
||||
}
|
||||
|
||||
void f(size_t size)
|
||||
@@ -148,31 +124,32 @@ void f(size_t size)
|
||||
auto t7 = std::thread(calloc3, size);
|
||||
auto t8 = std::thread(calloc4, size);
|
||||
|
||||
auto a = snmalloc::current_alloc_pool()->acquire();
|
||||
auto p1 = a->alloc(size);
|
||||
auto p2 = a->alloc(size);
|
||||
auto p3 = a->alloc(size);
|
||||
auto p4 = a->alloc(size);
|
||||
{
|
||||
auto a = snmalloc::get_scoped_allocator();
|
||||
auto p1 = a->alloc(size);
|
||||
auto p2 = a->alloc(size);
|
||||
auto p3 = a->alloc(size);
|
||||
auto p4 = a->alloc(size);
|
||||
|
||||
auto t9 = std::thread(dealloc1, p1, size);
|
||||
auto t10 = std::thread(dealloc2, p2, size);
|
||||
auto t11 = std::thread(dealloc3, p3, size);
|
||||
auto t12 = std::thread(dealloc4, p4, size);
|
||||
auto t9 = std::thread(dealloc1, p1, size);
|
||||
auto t10 = std::thread(dealloc2, p2, size);
|
||||
auto t11 = std::thread(dealloc3, p3, size);
|
||||
auto t12 = std::thread(dealloc4, p4, size);
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
t3.join();
|
||||
t4.join();
|
||||
t5.join();
|
||||
t6.join();
|
||||
t7.join();
|
||||
t8.join();
|
||||
t9.join();
|
||||
t10.join();
|
||||
t11.join();
|
||||
t12.join();
|
||||
snmalloc::current_alloc_pool()->release(a);
|
||||
snmalloc::current_alloc_pool()->debug_in_use(0);
|
||||
t1.join();
|
||||
t2.join();
|
||||
t3.join();
|
||||
t4.join();
|
||||
t5.join();
|
||||
t6.join();
|
||||
t7.join();
|
||||
t8.join();
|
||||
t9.join();
|
||||
t10.join();
|
||||
t11.join();
|
||||
t12.join();
|
||||
} // Drops a.
|
||||
// snmalloc::current_alloc_pool()->debug_in_use(0);
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
}
|
||||
@@ -183,16 +160,13 @@ int main(int, char**)
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
|
||||
std::thread t(get_test);
|
||||
t.join();
|
||||
|
||||
f(0);
|
||||
f(1);
|
||||
f(3);
|
||||
f(5);
|
||||
f(7);
|
||||
printf("\n");
|
||||
for (size_t exp = 1; exp < snmalloc::SUPERSLAB_BITS; exp++)
|
||||
for (size_t exp = 1; exp < snmalloc::MAX_SIZECLASS_BITS; exp++)
|
||||
{
|
||||
auto shifted = [exp](size_t v) { return v << exp; };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user