Merge pull request #21 from plietar/smart-next

Place the next pointer at a different place on every object.
This commit is contained in:
Matthew Parkinson
2019-02-19 09:04:23 +00:00
committed by GitHub
6 changed files with 143 additions and 31 deletions

View File

@@ -628,6 +628,26 @@ namespace snmalloc
std::conditional_t<IsQueueInline, RemoteAllocator, RemoteAllocator*>
remote_alloc;
#ifdef CACHE_FRIENDLY_OFFSET
size_t remote_offset = 0;
void* apply_cache_friendly_offset(void* p, uint8_t sizeclass)
{
size_t mask = sizeclass_to_cache_friendly_mask(sizeclass);
size_t offset = remote_offset & mask;
remote_offset += CACHE_FRIENDLY_OFFSET;
return (void*)((uintptr_t)p + offset);
}
#else
void* apply_cache_friendly_offset(void* p, uint8_t sizeclass)
{
UNUSED(sizeclass);
return p;
}
#endif
auto* public_state()
{
if constexpr (IsQueueInline)
@@ -726,7 +746,7 @@ namespace snmalloc
Metaslab& meta = super->get_meta(slab);
if (p->target_id() == id())
{
small_dealloc(super, p, meta.sizeclass);
small_dealloc_offseted(super, p, meta.sizeclass);
}
else
{
@@ -739,7 +759,9 @@ namespace snmalloc
Mediumslab* slab = Mediumslab::get(p);
if (p->target_id() == id())
{
medium_dealloc(slab, p, slab->get_sizeclass());
uint8_t sizeclass = slab->get_sizeclass();
void* start = remove_cache_friendly_offset(p, sizeclass);
medium_dealloc(slab, start, sizeclass);
}
else
{
@@ -909,6 +931,20 @@ namespace snmalloc
void small_dealloc(Superslab* super, void* p, uint8_t sizeclass)
{
#ifndef SNMALLOC_SAFE_CLIENT
Slab* slab = Slab::get(p);
if (!slab->is_start_of_object(super, p))
{
error("Not deallocating start of an object");
}
#endif
void* offseted = apply_cache_friendly_offset(p, sizeclass);
small_dealloc_offseted(super, offseted, sizeclass);
}
void small_dealloc_offseted(Superslab* super, void* p, uint8_t sizeclass)
{
MEASURE_TIME(small_dealloc, 4, 16);
stats().sizeclass_dealloc(sizeclass);
@@ -1111,8 +1147,10 @@ namespace snmalloc
{
MEASURE_TIME(remote_dealloc, 4, 16);
void* offseted = apply_cache_friendly_offset(p, sizeclass);
stats().remote_free(sizeclass);
remote.dealloc(target->id(), p, sizeclass);
remote.dealloc(target->id(), offseted, sizeclass);
if (remote.size < REMOTE_CACHE)
return;

View File

@@ -102,7 +102,12 @@ namespace snmalloc
{
size_t size = sizeclass_to_size(sizeclass);
size_t offset = get_slab_offset(sizeclass, is_short);
return ((((head & ~(size_t)1) - (offset & ~(size_t)1)) % size) == 0);
size_t head_start =
remove_cache_friendly_offset(head & ~(size_t)1, sizeclass);
size_t slab_start = offset & ~(size_t)1;
return ((head_start - slab_start) % size) == 0;
}
void debug_slab_invariant(bool is_short, Slab* slab)
@@ -129,7 +134,9 @@ namespace snmalloc
while ((curr & 1) != 1)
{
// Check we are looking at a correctly aligned block
assert((curr - offset) % size == 0);
uint16_t start = remove_cache_friendly_offset(curr, sizeclass);
assert((start - offset) % size == 0);
// Account for free elements in free list
accounted_for += size;
assert(SLAB_SIZE >= accounted_for);
@@ -143,7 +150,8 @@ namespace snmalloc
}
// Check we terminated traversal on a correctly aligned block
assert(((curr & ~1) - offset) % size == 0);
uint16_t start = remove_cache_friendly_offset(curr & ~1, sizeclass);
assert((start - offset) % size == 0);
if (curr != link)
{

View File

@@ -6,6 +6,8 @@ namespace snmalloc
{
constexpr static uint16_t get_slab_offset(uint8_t sc, bool is_short);
constexpr static size_t sizeclass_to_size(uint8_t sizeclass);
constexpr static size_t sizeclass_to_cache_friendly_mask(uint8_t sizeclass);
constexpr static size_t sizeclass_to_inverse_cache_friendly_mask(uint8_t sc);
constexpr static uint16_t medium_slab_free(uint8_t sizeclass);
static inline uint8_t size_to_sizeclass(size_t size)
@@ -137,4 +139,31 @@ namespace snmalloc
// everything fits into 32bits here.
return (uint32_t)(offset % rsize) == 0;
}
#ifdef CACHE_FRIENDLY_OFFSET
inline static void* remove_cache_friendly_offset(void* p, uint8_t sizeclass)
{
size_t mask = sizeclass_to_inverse_cache_friendly_mask(sizeclass);
return p = (void*)((uintptr_t)p & mask);
}
inline static uint16_t
remove_cache_friendly_offset(uint16_t relative, uint8_t sizeclass)
{
size_t mask = sizeclass_to_inverse_cache_friendly_mask(sizeclass);
return relative & mask;
}
#else
inline static void* remove_cache_friendly_offset(void* p, uint8_t sizeclass)
{
UNUSED(sizeclass);
return p;
}
inline static uint16_t
remove_cache_friendly_offset(uint16_t relative, uint8_t sizeclass)
{
UNUSED(sizeclass);
return relative;
}
#endif
};

View File

@@ -8,6 +8,8 @@ namespace snmalloc
struct SizeClassTable
{
ModArray<NUM_SIZECLASSES, size_t> size;
ModArray<NUM_SIZECLASSES, size_t> cache_friendly_mask;
ModArray<NUM_SIZECLASSES, size_t> inverse_cache_friendly_mask;
ModArray<NUM_SMALL_CLASSES, uint16_t> bump_ptr_start;
ModArray<NUM_SMALL_CLASSES, uint16_t> short_bump_ptr_start;
ModArray<NUM_SMALL_CLASSES, uint16_t> count_per_slab;
@@ -15,6 +17,8 @@ namespace snmalloc
constexpr SizeClassTable()
: size(),
cache_friendly_mask(),
inverse_cache_friendly_mask(),
bump_ptr_start(),
short_bump_ptr_start(),
count_per_slab(),
@@ -24,6 +28,11 @@ namespace snmalloc
{
size[sizeclass] =
bits::from_exp_mant<INTERMEDIATE_BITS, MIN_ALLOC_BITS>(sizeclass);
size_t alignment =
std::min((size_t)1 << bits::ctz_const(size[sizeclass]), OS_PAGE_SIZE);
cache_friendly_mask[sizeclass] = (alignment - 1);
inverse_cache_friendly_mask[sizeclass] = ~(alignment - 1);
}
size_t header_size = sizeof(Superslab);
@@ -60,6 +69,18 @@ namespace snmalloc
return sizeclass_metadata.size[sizeclass];
}
constexpr static inline size_t
sizeclass_to_cache_friendly_mask(uint8_t sizeclass)
{
return sizeclass_metadata.cache_friendly_mask[sizeclass];
}
constexpr static inline size_t
sizeclass_to_inverse_cache_friendly_mask(uint8_t sizeclass)
{
return sizeclass_metadata.inverse_cache_friendly_mask[sizeclass];
}
constexpr static inline size_t sizeclass_to_count(uint8_t sizeclass)
{
return sizeclass_metadata.count_per_slab[sizeclass];

View File

@@ -48,11 +48,13 @@ namespace snmalloc
if ((head & 1) == 0)
{
p = (void*)((size_t)this + head);
void* node = (void*)((size_t)this + head);
// Read the next slot from the memory that's about to be allocated.
uint16_t next = *(uint16_t*)p;
uint16_t next = *(uint16_t*)node;
meta.head = next;
p = remove_cache_friendly_offset(node, meta.sizeclass);
}
else
{
@@ -82,6 +84,14 @@ namespace snmalloc
return p;
}
bool is_start_of_object(Superslab* super, void* p)
{
Metaslab& meta = super->get_meta(this);
return is_multiple_of_sizeclass(
sizeclass_to_size(meta.sizeclass),
(uintptr_t)this + SLAB_SIZE - (uintptr_t)p);
}
// Returns true, if it alters get_status.
template<typename MemoryProvider>
inline typename Superslab::Action dealloc(
@@ -93,15 +103,6 @@ namespace snmalloc
meta.debug_slab_invariant(is_short(), this);
meta.sub_use();
#ifndef SNMALLOC_SAFE_CLIENT
if (!is_multiple_of_sizeclass(
sizeclass_to_size(meta.sizeclass),
(uintptr_t)this + SLAB_SIZE - (uintptr_t)p))
{
error("Not deallocating start of an object");
}
#endif
if (was_full)
{
// We are not on the sizeclass list.