From 9e8f018234beaa310c4ee36dcf6b93ae8dc88d28 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sun, 27 Jul 2025 21:29:38 +0300 Subject: [PATCH 1/3] [wasm] Disable allocation from los sections Allocation in the segments is normally done for objects smaller than 1MB. The problem is that for los sections we require 1MB alignment and on wasm we end up allocating twice the memory in order to ensure the alignment. With this commit we reuse the same allocation code that was used for objects bigger than 1MB. This still uses the unfortunate alignment of page size. --- src/mono/mono/sgen/sgen-los.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/sgen/sgen-los.c b/src/mono/mono/sgen/sgen-los.c index e39eade5922f45..9dae4c3ade319f 100644 --- a/src/mono/mono/sgen/sgen-los.c +++ b/src/mono/mono/sgen/sgen-los.c @@ -125,6 +125,12 @@ mword sgen_los_memory_usage = 0; /* Total memory used by the LOS allocator */ mword sgen_los_memory_usage_total = 0; +#ifdef HOST_WASM +const gboolean sgen_los_enable_sections_allocator = 0; +#else +const gboolean sgen_los_enable_sections_allocator = 1; +#endif + static LOSSection *los_sections = NULL; static LOSFreeChunks *los_fast_free_lists [LOS_NUM_FAST_SIZES]; /* 0 is for larger sizes */ static mword los_num_objects = 0; @@ -156,6 +162,9 @@ los_consistency_check (void) int i; mword memory_usage = 0; + if (!sgen_los_enable_sections_allocator) + return; + FOREACH_LOS_OBJECT_NO_LOCK (obj) { mword obj_size = sgen_los_object_size (obj); char *end = obj->data + obj_size; @@ -393,7 +402,7 @@ sgen_los_free_object (LOSObject *obj) #ifdef USE_MALLOC g_free (obj); #else - if (size > LOS_SECTION_OBJECT_LIMIT) { + if (!sgen_los_enable_sections_allocator || size > LOS_SECTION_OBJECT_LIMIT) { int pagesize = mono_pagesize (); size += sizeof (LOSObject); size = SGEN_ALIGN_UP_TO (size, pagesize); @@ -450,7 +459,7 @@ sgen_los_alloc_large_inner (GCVTable vtable, size_t size) obj = g_malloc (size + sizeof (LOSObject)); memset (obj, 0, size + sizeof (LOSObject)); #else - if (size > LOS_SECTION_OBJECT_LIMIT) { + if (!sgen_los_enable_sections_allocator || size > LOS_SECTION_OBJECT_LIMIT) { size_t obj_size = size + sizeof (LOSObject); int pagesize = mono_pagesize (); size_t alloc_size = SGEN_ALIGN_UP_TO (obj_size, pagesize); From 73b757a74705591e9caaab7ebc9f718ed6f8f105 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sun, 27 Jul 2025 21:49:21 +0300 Subject: [PATCH 2/3] [wasm] Avoid page size alignment for LOS objects smaller than LOS_SECTION_OBJECT_LIMIT On mono, objects need to have the alignment of 8 bytes. We allocate using this alignemnt directly for objects smaller than 1MB. This further decreases fragmentation and increases allocation speed. --- src/mono/mono/sgen/sgen-los.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/sgen/sgen-los.c b/src/mono/mono/sgen/sgen-los.c index 9dae4c3ade319f..77494b944da045 100644 --- a/src/mono/mono/sgen/sgen-los.c +++ b/src/mono/mono/sgen/sgen-los.c @@ -402,13 +402,18 @@ sgen_los_free_object (LOSObject *obj) #ifdef USE_MALLOC g_free (obj); #else - if (!sgen_los_enable_sections_allocator || size > LOS_SECTION_OBJECT_LIMIT) { + if (size > LOS_SECTION_OBJECT_LIMIT) { int pagesize = mono_pagesize (); size += sizeof (LOSObject); size = SGEN_ALIGN_UP_TO (size, pagesize); sgen_free_os_memory ((gpointer)SGEN_ALIGN_DOWN_TO ((mword)obj, pagesize), size, SGEN_ALLOC_HEAP, MONO_MEM_ACCOUNT_SGEN_LOS); sgen_los_memory_usage_total -= size; sgen_memgov_release_space (size, SPACE_LOS); + } else if (!sgen_los_enable_sections_allocator) { + size += sizeof (LOSObject); + sgen_free_os_memory (obj, size, SGEN_ALLOC_HEAP, MONO_MEM_ACCOUNT_SGEN_LOS); + sgen_los_memory_usage_total -= size; + sgen_memgov_release_space (size, SPACE_LOS); } else { free_los_section_memory (obj, size + sizeof (LOSObject)); #ifdef LOS_CONSISTENCY_CHECKS @@ -459,7 +464,7 @@ sgen_los_alloc_large_inner (GCVTable vtable, size_t size) obj = g_malloc (size + sizeof (LOSObject)); memset (obj, 0, size + sizeof (LOSObject)); #else - if (!sgen_los_enable_sections_allocator || size > LOS_SECTION_OBJECT_LIMIT) { + if (size > LOS_SECTION_OBJECT_LIMIT) { size_t obj_size = size + sizeof (LOSObject); int pagesize = mono_pagesize (); size_t alloc_size = SGEN_ALIGN_UP_TO (obj_size, pagesize); @@ -470,6 +475,13 @@ sgen_los_alloc_large_inner (GCVTable vtable, size_t size) obj = randomize_los_object_start (obj, obj_size, alloc_size, pagesize); } } + } else if (!sgen_los_enable_sections_allocator) { + size_t alloc_size = size + sizeof (LOSObject); + if (sgen_memgov_try_alloc_space (alloc_size, SPACE_LOS)) { + obj = (LOSObject *)sgen_alloc_os_memory_aligned (alloc_size, SGEN_ALLOC_ALIGN, (SgenAllocFlags)(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE), NULL, MONO_MEM_ACCOUNT_SGEN_LOS); + if (obj) + sgen_los_memory_usage_total += alloc_size; + } } else { obj = get_los_section_memory (size + sizeof (LOSObject)); if (obj) From d995297cefef9e123d0f018db74b2615951a5560 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 29 Jul 2025 14:53:04 +0300 Subject: [PATCH 3/3] Fix mapping of the same card to 2 different los objects This will produce some waste, but 512 alignment is still quite small compared to the size of the objects here (> 8k) --- src/mono/mono/sgen/sgen-los.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/sgen/sgen-los.c b/src/mono/mono/sgen/sgen-los.c index 77494b944da045..0ecedfaefa0ea1 100644 --- a/src/mono/mono/sgen/sgen-los.c +++ b/src/mono/mono/sgen/sgen-los.c @@ -478,7 +478,15 @@ sgen_los_alloc_large_inner (GCVTable vtable, size_t size) } else if (!sgen_los_enable_sections_allocator) { size_t alloc_size = size + sizeof (LOSObject); if (sgen_memgov_try_alloc_space (alloc_size, SPACE_LOS)) { - obj = (LOSObject *)sgen_alloc_os_memory_aligned (alloc_size, SGEN_ALLOC_ALIGN, (SgenAllocFlags)(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE), NULL, MONO_MEM_ACCOUNT_SGEN_LOS); +#ifdef SGEN_HAVE_OVERLAPPING_CARDS + int alignment = SGEN_ALLOC_ALIGN; +#else + // If we don't use the shadow card table, having a card map to 2 different los objects is invalid + // because once we scan the first object we could clear the card, leading to failure to detect refs + // in the second object. We prevent this by aligning the los object to the card size. + int alignment = CARD_SIZE_IN_BYTES; +#endif + obj = (LOSObject *)sgen_alloc_os_memory_aligned (alloc_size, alignment, (SgenAllocFlags)(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE), NULL, MONO_MEM_ACCOUNT_SGEN_LOS); if (obj) sgen_los_memory_usage_total += alloc_size; }