Skip to content

Commit a90c060

Browse files
Merge pull request #120 from microsoft/decommit_perf
Improvements to Decommit Strategies
2 parents afc77d9 + 053b5a3 commit a90c060

File tree

8 files changed

+65
-110
lines changed

8 files changed

+65
-110
lines changed

src/ds/helpers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ namespace snmalloc
5656
length == bits::next_pow2_const(length), "Must be a power of two.");
5757

5858
private:
59-
T value;
59+
T value = 0;
6060

6161
public:
6262
operator T()

src/mem/alloc.h

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -930,8 +930,7 @@ namespace snmalloc
930930

931931
if (super != nullptr)
932932
{
933-
Slab* slab =
934-
super->alloc_short_slab(sizeclass, large_allocator.memory_provider);
933+
Slab* slab = super->alloc_short_slab(sizeclass);
935934
assert(super->is_full());
936935
return slab;
937936
}
@@ -941,8 +940,7 @@ namespace snmalloc
941940
if ((allow_reserve == NoReserve) && (super == nullptr))
942941
return nullptr;
943942

944-
Slab* slab =
945-
super->alloc_short_slab(sizeclass, large_allocator.memory_provider);
943+
Slab* slab = super->alloc_short_slab(sizeclass);
946944
reposition_superslab(super);
947945
return slab;
948946
}
@@ -952,8 +950,7 @@ namespace snmalloc
952950
if ((allow_reserve == NoReserve) && (super == nullptr))
953951
return nullptr;
954952

955-
Slab* slab =
956-
super->alloc_slab(sizeclass, large_allocator.memory_provider);
953+
Slab* slab = super->alloc_slab(sizeclass);
957954
reposition_superslab(super);
958955
return slab;
959956
}
@@ -1074,7 +1071,7 @@ namespace snmalloc
10741071
SlabList* sl = &small_classes[sizeclass];
10751072
Slab* slab = Metaslab::get_slab(p);
10761073
Superslab::Action a =
1077-
slab->dealloc_slow(sl, super, p, large_allocator.memory_provider);
1074+
slab->dealloc_slow(sl, super, p);
10781075
if (likely(a == Superslab::NoSlabReturn))
10791076
return;
10801077
stats().sizeclass_dealloc_slab(sizeclass);
@@ -1114,20 +1111,6 @@ namespace snmalloc
11141111
{
11151112
super_available.remove(super);
11161113

1117-
if constexpr (decommit_strategy == DecommitSuper)
1118-
{
1119-
large_allocator.memory_provider.notify_not_using(
1120-
pointer_offset(super, OS_PAGE_SIZE),
1121-
SUPERSLAB_SIZE - OS_PAGE_SIZE);
1122-
}
1123-
else if constexpr (decommit_strategy == DecommitSuperLazy)
1124-
{
1125-
static_assert(
1126-
pal_supports<LowMemoryNotification, MemoryProvider>,
1127-
"A lazy decommit strategy cannot be implemented on platforms "
1128-
"without low memory notifications");
1129-
}
1130-
11311114
chunkmap().clear_slab(super);
11321115
large_allocator.dealloc(super, 0);
11331116
stats().superslab_push();
@@ -1191,7 +1174,7 @@ namespace snmalloc
11911174
{
11921175
MEASURE_TIME(medium_dealloc, 4, 16);
11931176
stats().sizeclass_dealloc(sizeclass);
1194-
bool was_full = slab->dealloc(p, large_allocator.memory_provider);
1177+
bool was_full = slab->dealloc(p);
11951178

11961179
#ifdef CHECK_CLIENT
11971180
if (!is_multiple_of_sizeclass(
@@ -1211,12 +1194,6 @@ namespace snmalloc
12111194
sc->remove(slab);
12121195
}
12131196

1214-
if constexpr (decommit_strategy == DecommitSuper)
1215-
{
1216-
large_allocator.memory_provider.notify_not_using(
1217-
pointer_offset(slab, OS_PAGE_SIZE), SUPERSLAB_SIZE - OS_PAGE_SIZE);
1218-
}
1219-
12201197
chunkmap().clear_slab(slab);
12211198
large_allocator.dealloc(slab, 0);
12221199
stats().superslab_push();
@@ -1264,19 +1241,13 @@ namespace snmalloc
12641241
MEASURE_TIME(large_dealloc, 4, 16);
12651242

12661243
size_t size_bits = bits::next_pow2_bits(size);
1267-
size_t rsize = bits::one_at_bit(size_bits);
1268-
assert(rsize >= SUPERSLAB_SIZE);
1244+
assert(bits::one_at_bit(size_bits) >= SUPERSLAB_SIZE);
12691245
size_t large_class = size_bits - SUPERSLAB_BITS;
12701246

12711247
chunkmap().clear_large_size(p, size);
12721248

12731249
stats().large_dealloc(large_class);
12741250

1275-
// Cross-reference largealloc's alloc() decommitted condition.
1276-
if ((decommit_strategy != DecommitNone) || (large_class > 0))
1277-
large_allocator.memory_provider.notify_not_using(
1278-
pointer_offset(p, OS_PAGE_SIZE), rsize - OS_PAGE_SIZE);
1279-
12801251
// Initialise in order to set the correct SlabKind.
12811252
Largeslab* slab = static_cast<Largeslab*>(p);
12821253
slab->init();

src/mem/allocconfig.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,6 @@ namespace snmalloc
6868
* Decommit superslabs when they are entirely empty.
6969
*/
7070
DecommitSuper,
71-
/**
72-
* Decommit all slabs once they are empty.
73-
*/
74-
DecommitAll,
7571
/**
7672
* Decommit superslabs only when we are informed of memory pressure by the
7773
* OS, do not decommit anything in normal operation.

src/mem/largealloc.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ namespace snmalloc
8080
*/
8181
std::atomic<uint64_t> last_low_memory_epoch = 0;
8282
std::atomic_flag lazy_decommit_guard;
83-
void lazy_decommit()
83+
SNMALLOC_SLOW_PATH void lazy_decommit()
8484
{
8585
// If another thread is try to do lazy decommit, let it continue. If
8686
// we try to parallelise this, we'll most likely end up waiting on the
@@ -93,6 +93,7 @@ namespace snmalloc
9393
// the memory that we can. Start with the small size classes so that we
9494
// hit cached superslabs first.
9595
// FIXME: We probably shouldn't do this all at once.
96+
// FIXME: We currently Decommit all the sizeclasses larger than 0.
9697
for (size_t large_class = 0; large_class < NUM_LARGE_CLASSES;
9798
large_class++)
9899
{
@@ -327,7 +328,8 @@ namespace snmalloc
327328
void* alloc(size_t large_class, size_t size)
328329
{
329330
size_t rsize = bits::one_at_bit(SUPERSLAB_BITS) << large_class;
330-
if (size == 0)
331+
// For superslab size, we always commit the whole range.
332+
if (large_class == 0)
331333
size = rsize;
332334

333335
void* p = memory_provider.large_stack[large_class].pop();
@@ -362,7 +364,7 @@ namespace snmalloc
362364
bool decommitted =
363365
((decommit_strategy == DecommitSuperLazy) &&
364366
(static_cast<Baseslab*>(p)->get_kind() == Decommitted)) ||
365-
(large_class > 0) || (decommit_strategy != DecommitNone);
367+
(large_class > 0) || (decommit_strategy == DecommitSuper);
366368

367369
if (decommitted)
368370
{
@@ -392,6 +394,24 @@ namespace snmalloc
392394

393395
void dealloc(void* p, size_t large_class)
394396
{
397+
if constexpr (decommit_strategy == DecommitSuperLazy)
398+
{
399+
static_assert(
400+
pal_supports<LowMemoryNotification, MemoryProvider>,
401+
"A lazy decommit strategy cannot be implemented on platforms "
402+
"without low memory notifications");
403+
}
404+
405+
// Cross-reference largealloc's alloc() decommitted condition.
406+
if ((decommit_strategy != DecommitNone)
407+
&& (large_class != 0 || decommit_strategy == DecommitSuper))
408+
{
409+
size_t rsize = bits::one_at_bit(SUPERSLAB_BITS) << large_class;
410+
411+
memory_provider.notify_not_using(
412+
pointer_offset(p, OS_PAGE_SIZE), rsize - OS_PAGE_SIZE);
413+
}
414+
395415
stats.superslab_push();
396416
memory_provider.large_stack[large_class].push(static_cast<Largeslab*>(p));
397417
memory_provider.lazy_decommit_if_needed();

src/mem/mediumslab.h

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,13 @@ namespace snmalloc
8787
assert(is_aligned_block<OS_PAGE_SIZE>(p, OS_PAGE_SIZE));
8888
size = bits::align_up(size, OS_PAGE_SIZE);
8989

90-
if constexpr (decommit_strategy == DecommitAll)
91-
memory_provider.template notify_using<zero_mem>(p, size);
92-
else if constexpr (zero_mem == YesZero)
90+
if constexpr (zero_mem == YesZero)
9391
memory_provider.template zero<true>(p, size);
9492

9593
return p;
9694
}
9795

98-
template<typename MemoryProvider>
99-
bool dealloc(void* p, MemoryProvider& memory_provider)
96+
bool dealloc(void* p)
10097
{
10198
assert(head > 0);
10299

@@ -105,9 +102,6 @@ namespace snmalloc
105102
free++;
106103
stack[--head] = pointer_to_index(p);
107104

108-
if constexpr (decommit_strategy == DecommitAll)
109-
memory_provider.notify_not_using(p, sizeclass_to_size(sizeclass));
110-
111105
return was_full;
112106
}
113107

src/mem/metaslab.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace snmalloc
3636
* The list will be (allocated - needed - 1) long. The -1 is
3737
* for the `link` element which is not in the free list.
3838
*/
39-
void* head;
39+
void* head = nullptr;
4040

4141
/**
4242
* How many entries are not in the free list of slab, i.e.
@@ -51,7 +51,7 @@ namespace snmalloc
5151
/**
5252
* How many entries have been allocated from this slab.
5353
*/
54-
uint16_t allocated;
54+
uint16_t allocated = 0;
5555

5656
// When a slab has free space it will be on the has space list for
5757
// that size class. We use an empty block in this slab to be the

src/mem/slab.h

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ namespace snmalloc
6868
}
6969
else
7070
{
71+
// Allocate the last object on the current page if there is one,
72+
// and then thread the next free list worth of allocations.
73+
bool crossed_page_boundary = false;
7174
void* curr = nullptr;
72-
bool commit = false;
7375
while (true)
7476
{
7577
size_t newbumpptr = bumpptr + rsize;
@@ -78,15 +80,12 @@ namespace snmalloc
7880

7981
if (alignedbumpptr != alignednewbumpptr)
8082
{
81-
// We have committed once already.
82-
if (commit)
83+
// We have crossed a page boundary already, so
84+
// lets stop building our free list.
85+
if (crossed_page_boundary)
8386
break;
8487

85-
memory_provider.template notify_using<NoZero>(
86-
pointer_offset(this, alignedbumpptr),
87-
alignednewbumpptr - alignedbumpptr);
88-
89-
commit = true;
88+
crossed_page_boundary = true;
9089
}
9190

9291
if (curr == nullptr)
@@ -179,9 +178,8 @@ namespace snmalloc
179178
// This does not need to remove the "use" as done by the fast path.
180179
// Returns a complex return code for managing the superslab meta data.
181180
// i.e. This deallocation could make an entire superslab free.
182-
template<typename MemoryProvider>
183181
SNMALLOC_SLOW_PATH typename Superslab::Action dealloc_slow(
184-
SlabList* sl, Superslab* super, void* p, MemoryProvider& memory_provider)
182+
SlabList* sl, Superslab* super, void* p)
185183
{
186184
Metaslab& meta = super->get_meta(this);
187185

@@ -192,9 +190,9 @@ namespace snmalloc
192190
{
193191
// Dealloc on the superslab.
194192
if (is_short())
195-
return super->dealloc_short_slab(memory_provider);
193+
return super->dealloc_short_slab();
196194

197-
return super->dealloc_slab(this, memory_provider);
195+
return super->dealloc_slab(this);
198196
}
199197
// Update the head and the sizeclass link.
200198
uint16_t index = pointer_to_index(p);
@@ -213,10 +211,11 @@ namespace snmalloc
213211
sl->remove(meta.get_link(this));
214212

215213
if (is_short())
216-
return super->dealloc_short_slab(memory_provider);
214+
return super->dealloc_short_slab();
217215

218-
return super->dealloc_slab(this, memory_provider);
216+
return super->dealloc_slab(this);
219217
}
218+
220219
bool is_short()
221220
{
222221
return Metaslab::is_short(this);

0 commit comments

Comments
 (0)