Merge pull request #13 from Microsoft/matt-refactor

Simplification around remote allocation
This commit is contained in:
Matthew Parkinson
2019-02-06 11:25:07 +00:00
committed by GitHub
3 changed files with 48 additions and 73 deletions

View File

@@ -522,16 +522,27 @@ namespace snmalloc
size_t size = 0;
RemoteList list[REMOTE_SLOTS];
/// Used to find the index into the array of queues for remote
/// deallocation
/// r is used for which round of sending this is.
inline size_t get_slot(size_t id, size_t r)
{
constexpr size_t allocator_size =
sizeof(Allocator<MemoryProvider, PageMap, IsQueueInline>);
constexpr size_t initial_shift =
bits::next_pow2_bits_const(allocator_size);
return (id >> (initial_shift + (r * REMOTE_SLOT_BITS))) & REMOTE_MASK;
}
void dealloc(alloc_id_t target_id, void* p, uint8_t sizeclass)
{
this->size += sizeclass_to_size(sizeclass);
Remote* r = (Remote*)p;
r->set_sizeclass_and_target_id(target_id, sizeclass);
assert(r->sizeclass() == sizeclass);
r->set_target_id(target_id);
assert(r->target_id() == target_id);
RemoteList* l = &list[target_id & REMOTE_MASK];
RemoteList* l = &list[get_slot(target_id, 0)];
l->last->non_atomic_next = r;
l->last = r;
}
@@ -541,11 +552,11 @@ namespace snmalloc
// When the cache gets big, post lists to their target allocators.
size = 0;
size_t shift = 0;
size_t post_round = 0;
while (true)
{
auto my_slot = (id >> shift) & REMOTE_MASK;
auto my_slot = get_slot(id, post_round);
for (size_t i = 0; i < REMOTE_SLOTS; i++)
{
@@ -575,13 +586,13 @@ namespace snmalloc
resend->last->non_atomic_next = nullptr;
resend->clear();
shift += REMOTE_SLOT_BITS;
post_round++;
while (r != nullptr)
{
// Use the next N bits to spread out remote deallocs in our own
// slot.
size_t slot = (r->target_id() >> shift) & REMOTE_MASK;
size_t slot = get_slot(r->target_id(), post_round);
RemoteList* l = &list[slot];
l->last->non_atomic_next = r;
l->last = r;
@@ -693,21 +704,34 @@ namespace snmalloc
{
if (p != &stub)
{
uint8_t sizeclass = p->sizeclass();
Superslab* super = Superslab::get(p);
if (p->target_id() == id())
if (super->get_kind() == Super)
{
stats().remote_receive(sizeclass);
if (sizeclass < NUM_SMALL_CLASSES)
small_dealloc(Superslab::get(p), p, sizeclass);
Slab* slab = Slab::get(p);
Metaslab& meta = super->get_meta(slab);
if (p->target_id() == id())
{
small_dealloc(super, p, meta.sizeclass);
}
else
medium_dealloc(Mediumslab::get(p), p, sizeclass);
{
// Queue for remote dealloc elsewhere.
remote.dealloc(p->target_id(), p, meta.sizeclass);
}
}
else
{
// Queue for remote dealloc elsewhere.
remote.dealloc(p->target_id(), p, sizeclass);
Mediumslab* slab = Mediumslab::get(p);
if (p->target_id() == id())
{
medium_dealloc(slab, p, slab->get_sizeclass());
}
else
{
// Queue for remote dealloc elsewhere.
remote.dealloc(p->target_id(), p, slab->get_sizeclass());
}
}
}
}

View File

@@ -50,7 +50,8 @@ namespace snmalloc
if (p != nullptr)
return p;
p = (T*)memory_provider.alloc_chunk(sizeof(T));
p = (T*)memory_provider
.template alloc_chunk<bits::next_pow2_const(sizeof(T))>(sizeof(T));
new (p) T(std::forward<Args...>(args)...);

View File

@@ -10,13 +10,6 @@ namespace snmalloc
{
struct Remote
{
static const size_t PTR_BITS = sizeof(void*) * 8;
static const size_t SIZECLASS_BITS =
bits::next_pow2_bits_const(NUM_SIZECLASSES);
static const bool USE_TOP_BITS =
SIZECLASS_BITS + bits::ADDRESS_BITS < PTR_BITS;
static const uintptr_t SIZECLASS_MASK = ((1ULL << SIZECLASS_BITS) - 1);
using alloc_id_t = size_t;
union
{
@@ -24,65 +17,22 @@ namespace snmalloc
Remote* non_atomic_next;
};
// Uses an intptr_t so that when we use the TOP_BITS to encode the
// sizeclass, then we can use signed shift to correctly handle kernel versus
// user mode.
intptr_t value;
alloc_id_t allocator_id;
// This will not exist for the minimum object size. This is only used if
// USE_TOP_BITS is false, and the bottom bit of value is set.
uint8_t possible_sizeclass;
void set_sizeclass_and_target_id(alloc_id_t id, uint8_t sizeclass)
void set_target_id(alloc_id_t id)
{
if constexpr (USE_TOP_BITS)
{
value = (intptr_t)(
(id << SIZECLASS_BITS) | ((static_cast<uintptr_t>(sizeclass))));
}
else
{
assert((id & 1) == 0);
if (sizeclass == 0)
{
value = (intptr_t)(id | 1);
}
else
{
value = (intptr_t)id;
possible_sizeclass = sizeclass;
}
}
allocator_id = id;
}
alloc_id_t target_id()
{
if constexpr (USE_TOP_BITS)
{
return (alloc_id_t)(value >> SIZECLASS_BITS);
}
else
{
return (alloc_id_t)(value & ~1);
}
}
uint8_t sizeclass()
{
if constexpr (USE_TOP_BITS)
{
return (static_cast<uint8_t>((uintptr_t)value & SIZECLASS_MASK));
}
else
{
return ((value & 1) == 1) ? 0 : possible_sizeclass;
}
return allocator_id;
}
};
static_assert(
(offsetof(Remote, possible_sizeclass)) <= MIN_ALLOC_SIZE,
"Need to be able to cast any small alloc to Remote");
sizeof(Remote) <= MIN_ALLOC_SIZE,
"Needs to be able to fit in smallest allocation.");
struct RemoteAllocator
{