Skip to content

Commit 990b754

Browse files
authored
emmalloc: Fix allocations of high alignment (#20704)
Take the alignment into account when requesting new memory from the OS. If the alignment is high then we need to ask for enough to ensure we end up aligned, or else we can end up allocating double the memory we need (see #20645). This only changes the amount we allocate from the OS if the alignment is non- standard, that is, this is NFC for normal calls to malloc. Also remove an assertion that limited the maximum alignment. mimalloc wants 4 MB alignment. Fixes #20654
1 parent 326600a commit 990b754

10 files changed

+80
-30
lines changed

system/lib/emmalloc.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -671,12 +671,9 @@ static void *attempt_allocate(Region *freeRegion, size_t alignment, size_t size)
671671

672672
static size_t validate_alloc_alignment(size_t alignment)
673673
{
674-
// Cannot perform allocations that are less than 4 byte aligned, because the Region
675-
// control structures need to be aligned. Also round up to minimum outputted alignment.
676-
alignment = MAX(alignment, MALLOC_ALIGNMENT);
677-
// Arbitrary upper limit on alignment - very likely a programming bug if alignment is higher than this.
678-
assert(alignment <= 1024*1024);
679-
return alignment;
674+
// Cannot perform allocations that are less our minimal alignment, because
675+
// the Region control structures need to be aligned themselves.
676+
return MAX(alignment, MALLOC_ALIGNMENT);
680677
}
681678

682679
static size_t validate_alloc_size(size_t size)
@@ -808,6 +805,22 @@ static void *allocate_memory(size_t alignment, size_t size)
808805

809806
// We were unable to find a free memory region. Must sbrk() in more memory!
810807
size_t numBytesToClaim = size+sizeof(Region)*3;
808+
// Take into account the alignment as well. For typical alignment we don't
809+
// need to add anything here (so we do nothing if the alignment is equal to
810+
// MALLOC_ALIGNMENT), but it can matter if the alignment is very high. In that
811+
// case, not adding the alignment can lead to this sbrk not giving us enough
812+
// (in which case, the next attempt fails and will sbrk the same amount again,
813+
// potentially allocating a lot more memory than necessary).
814+
//
815+
// Note that this is not necessarily optimal, as the extra allocation size for
816+
// the alignment might not be needed (if we are lucky and already aligned),
817+
// and even if it helps us allocate it will not immediately be ready for reuse
818+
// (as it will be added to the currently-in-use region before us, so it is not
819+
// in a free list). As a compromise however it seems reasonable in practice as
820+
// a way to handle large aligned regions to avoid even worse waste.
821+
if (alignment > MALLOC_ALIGNMENT) {
822+
numBytesToClaim += alignment;
823+
}
811824
assert(numBytesToClaim > size); // 32-bit wraparound should not happen here, allocation size has been validated above!
812825
bool success = claim_more_memory(numBytesToClaim);
813826
if (success)

test/code_size/embind_val_wasm.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"a.html.gz": 431,
44
"a.js": 7498,
55
"a.js.gz": 3142,
6-
"a.wasm": 11533,
7-
"a.wasm.gz": 5767,
8-
"total": 19704,
9-
"total_gz": 9340
6+
"a.wasm": 11515,
7+
"a.wasm.gz": 5771,
8+
"total": 19686,
9+
"total_gz": 9344
1010
}

test/code_size/hello_wasm_worker_wasm.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"a.html.gz": 433,
44
"a.js": 667,
55
"a.js.gz": 458,
6-
"a.wasm": 1863,
7-
"a.wasm.gz": 1051,
8-
"total": 3267,
9-
"total_gz": 1942
6+
"a.wasm": 1858,
7+
"a.wasm.gz": 1058,
8+
"total": 3262,
9+
"total_gz": 1949
1010
}

test/code_size/hello_webgl2_wasm.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"a.html.gz": 379,
44
"a.js": 4699,
55
"a.js.gz": 2419,
6-
"a.wasm": 10475,
7-
"a.wasm.gz": 6710,
8-
"total": 15743,
9-
"total_gz": 9508
6+
"a.wasm": 10468,
7+
"a.wasm.gz": 6719,
8+
"total": 15736,
9+
"total_gz": 9517
1010
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"a.html": 567,
33
"a.html.gz": 379,
4-
"a.js": 17921,
5-
"a.js.gz": 8079,
4+
"a.js": 17841,
5+
"a.js.gz": 8088,
66
"a.mem": 3171,
77
"a.mem.gz": 2713,
8-
"total": 21659,
9-
"total_gz": 11171
8+
"total": 21579,
9+
"total_gz": 11180
1010
}

test/code_size/hello_webgl_wasm.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"a.html.gz": 379,
44
"a.js": 4185,
55
"a.js.gz": 2243,
6-
"a.wasm": 10475,
7-
"a.wasm.gz": 6710,
8-
"total": 15229,
9-
"total_gz": 9332
6+
"a.wasm": 10468,
7+
"a.wasm.gz": 6719,
8+
"total": 15222,
9+
"total_gz": 9341
1010
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"a.html": 567,
33
"a.html.gz": 379,
4-
"a.js": 17399,
5-
"a.js.gz": 7910,
4+
"a.js": 17319,
5+
"a.js.gz": 7908,
66
"a.mem": 3171,
77
"a.mem.gz": 2713,
8-
"total": 21137,
9-
"total_gz": 11002
8+
"total": 21057,
9+
"total_gz": 11000
1010
}

test/other/test_emmalloc_high_align.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <assert.h>
2+
#include <emscripten/console.h>
3+
#include <stdlib.h>
4+
#include <unistd.h>
5+
6+
size_t sizeT(void* p) {
7+
return (size_t)p;
8+
}
9+
10+
int main() {
11+
const size_t MB = 1024 * 1024;
12+
const size_t ALIGN = 4 * MB;
13+
const size_t SIZE = 32 * MB;
14+
15+
// Allocate a very large chunk of memory (32MB) with very high alignment (4
16+
// MB). This is similar to what mimalloc does in practice.
17+
void* before = sbrk(0);
18+
void* p = aligned_alloc(ALIGN, SIZE);
19+
void* after = sbrk(0);
20+
emscripten_console_logf("before: %p after: %p p: %p\n", before, after, p);
21+
22+
// The allocation must be properly aligned.
23+
assert(sizeT(p) % ALIGN == 0);
24+
25+
// We should only have sbrk'd a reasonable amount (this is a regression test
26+
// for #20645 where we sbrk'd double the necessary amount). We expect at most
27+
// 36 MB (the size of the allocation plus the alignment) plus a few bytes of
28+
// general overhead.
29+
assert(sizeT(after) - sizeT(before) < ALIGN + SIZE + 100);
30+
31+
emscripten_console_log("success");
32+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
success

test/test_other.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7494,6 +7494,10 @@ def test(args, text=None):
74947494
test(['-sALLOW_MEMORY_GROWTH', '-sMAXIMUM_MEMORY=1GB'])
74957495
test(['-sALLOW_MEMORY_GROWTH', '-sMAXIMUM_MEMORY=4GB'])
74967496

7497+
def test_emmalloc_high_align(self):
7498+
self.do_other_test('test_emmalloc_high_align.c',
7499+
emcc_args=['-sMALLOC=emmalloc', '-sINITIAL_MEMORY=128MB'])
7500+
74977501
def test_2GB_plus(self):
74987502
# when the heap size can be over 2GB, we rewrite pointers to be unsigned
74997503
def test(page_diff):

0 commit comments

Comments
 (0)