commit c5c8110cd319bfa6da73e0b02de3c34588c2514a
parent f7162eb3ae15099468a5d839246b2adac06ffcdd
Author: Jon Coppeard <jcoppeard@mozilla.com>
Date: Mon, 6 Oct 2025 09:51:10 +0000
Bug 1978645 - Part 0: Add argument for max nursery size to buffer allocation APIs r=sfink
Wasm GC allocations tend to get tenured more often than regular JS objects
which means minor GCs can spend a lot of time moving these directly allocated
buffers. This increases minor GC time which also means we keep the nursery size
smaller, affecting performance. This adds an option to set the maximum size of
buffer allocations made directly in the nursery. For Wasm GC allocations we'll
make this smaller than the regular size.
Differential Revision: https://phabricator.services.mozilla.com/D267390
Diffstat:
3 files changed, 51 insertions(+), 41 deletions(-)
diff --git a/js/src/gc/Nursery-inl.h b/js/src/gc/Nursery-inl.h
@@ -301,6 +301,28 @@ inline void js::Nursery::unregisterTrailer(void* block) {
fromSpace.trailersRemovedUsed_++;
}
+inline void* js::Nursery::allocateBuffer(Zone* zone, js::gc::Cell* owner,
+ size_t nbytes, size_t maxNurserySize) {
+ MOZ_ASSERT(owner);
+ MOZ_ASSERT(zone == owner->zone());
+ MOZ_ASSERT(nbytes > 0);
+ MOZ_ASSERT(nbytes <= SIZE_MAX - gc::CellAlignBytes);
+ nbytes = RoundUp(nbytes, gc::CellAlignBytes);
+
+ if (!IsInsideNursery(owner)) {
+ return gc::AllocBuffer(zone, nbytes, false);
+ }
+
+ if (nbytes <= maxNurserySize) {
+ void* buffer = allocateInternalBuffer(zone, nbytes);
+ if (buffer) {
+ return buffer;
+ }
+ }
+
+ return gc::AllocBuffer(zone, nbytes, true);
+}
+
namespace js {
// The allocation methods below will not run the garbage collector. If the
@@ -328,18 +350,22 @@ static inline T* AllocNurseryOrMallocBuffer(JSContext* cx, gc::Cell* cell,
}
template <typename T>
-static inline T* AllocateCellBuffer(Nursery& nursery, JS::Zone* zone,
- gc::Cell* cell, uint32_t count) {
+static inline T* AllocateCellBuffer(
+ Nursery& nursery, JS::Zone* zone, gc::Cell* cell, uint32_t count,
+ size_t maxNurserySize = Nursery::MaxNurseryBufferSize) {
MOZ_ASSERT(zone == cell->zone());
size_t nbytes = RoundUp(count * sizeof(T), sizeof(Value));
- return static_cast<T*>(nursery.allocateBuffer(zone, cell, nbytes));
+ return static_cast<T*>(
+ nursery.allocateBuffer(zone, cell, nbytes, maxNurserySize));
}
template <typename T>
-static inline T* AllocateCellBuffer(JSContext* cx, gc::Cell* cell,
- uint32_t count) {
- T* buffer = AllocateCellBuffer<T>(cx->nursery(), cx->zone(), cell, count);
+static inline T* AllocateCellBuffer(
+ JSContext* cx, gc::Cell* cell, uint32_t count,
+ size_t maxNurserySize = Nursery::MaxNurseryBufferSize) {
+ T* buffer = AllocateCellBuffer<T>(cx->nursery(), cx->zone(), cell, count,
+ maxNurserySize);
if (!buffer) {
ReportOutOfMemory(cx);
return nullptr;
@@ -368,25 +394,27 @@ static inline T* ReallocNurseryOrMallocBuffer(JSContext* cx, gc::Cell* cell,
// If this returns null then the old buffer will be left alone.
template <typename T>
-static inline T* ReallocateCellBuffer(Nursery& nursery, JS::Zone* zone,
- gc::Cell* cell, T* oldBuffer,
- uint32_t oldCount, uint32_t newCount) {
+static inline T* ReallocateCellBuffer(
+ Nursery& nursery, JS::Zone* zone, gc::Cell* cell, T* oldBuffer,
+ uint32_t oldCount, uint32_t newCount,
+ size_t maxNurserySize = Nursery::MaxNurseryBufferSize) {
MOZ_ASSERT(zone == cell->zone());
size_t oldBytes = RoundUp(oldCount * sizeof(T), sizeof(Value));
size_t newBytes = RoundUp(newCount * sizeof(T), sizeof(Value));
- return static_cast<T*>(
- nursery.reallocateBuffer(zone, cell, oldBuffer, oldBytes, newBytes));
+ return static_cast<T*>(nursery.reallocateBuffer(
+ zone, cell, oldBuffer, oldBytes, newBytes, maxNurserySize));
}
// If this returns null then the old buffer will be left alone.
template <typename T>
-static inline T* ReallocateCellBuffer(JSContext* cx, gc::Cell* cell,
- T* oldBuffer, uint32_t oldCount,
- uint32_t newCount) {
- T* buffer = ReallocateCellBuffer<T>(cx->nursery(), cx->zone(), cell,
- oldBuffer, oldCount, newCount);
+static inline T* ReallocateCellBuffer(
+ JSContext* cx, gc::Cell* cell, T* oldBuffer, uint32_t oldCount,
+ uint32_t newCount, size_t maxNurserySize = Nursery::MaxNurseryBufferSize) {
+ T* buffer =
+ ReallocateCellBuffer<T>(cx->nursery(), cx->zone(), cell, oldBuffer,
+ oldCount, newCount, maxNurserySize);
if (!buffer) {
ReportOutOfMemory(cx);
}
diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp
@@ -826,27 +826,6 @@ void* js::Nursery::allocNurseryOrMallocBuffer(Zone* zone, Cell* owner,
return buffer;
}
-void* js::Nursery::allocateBuffer(Zone* zone, Cell* owner, size_t nbytes) {
- MOZ_ASSERT(owner);
- MOZ_ASSERT(zone == owner->zone());
- MOZ_ASSERT(nbytes > 0);
- MOZ_ASSERT(nbytes <= SIZE_MAX - gc::CellAlignBytes);
- nbytes = RoundUp(nbytes, gc::CellAlignBytes);
-
- if (!IsInsideNursery(owner)) {
- return AllocBuffer(zone, nbytes, false);
- }
-
- if (nbytes <= MaxNurseryBufferSize) {
- void* buffer = allocateInternalBuffer(zone, nbytes);
- if (buffer) {
- return buffer;
- }
- }
-
- return AllocBuffer(zone, nbytes, true);
-}
-
std::tuple<void*, bool> js::Nursery::allocateZeroedBuffer(Zone* zone,
size_t nbytes,
arena_id_t arena) {
@@ -920,7 +899,8 @@ void* js::Nursery::reallocNurseryOrMallocBuffer(Zone* zone, Cell* cell,
}
void* js::Nursery::reallocateBuffer(Zone* zone, Cell* cell, void* oldBuffer,
- size_t oldBytes, size_t newBytes) {
+ size_t oldBytes, size_t newBytes,
+ size_t maxNurserySize) {
if (!IsInsideNursery(cell)) {
MOZ_ASSERT(IsBufferAlloc(oldBuffer));
MOZ_ASSERT(!IsNurseryOwned(zone, oldBuffer));
@@ -937,7 +917,7 @@ void* js::Nursery::reallocateBuffer(Zone* zone, Cell* cell, void* oldBuffer,
return oldBuffer;
}
- auto newBuffer = allocateBuffer(zone, cell, newBytes);
+ auto newBuffer = allocateBuffer(zone, cell, newBytes, maxNurserySize);
if (newBuffer) {
PodCopy((uint8_t*)newBuffer, (uint8_t*)oldBuffer, oldBytes);
}
diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h
@@ -157,7 +157,8 @@ class Nursery {
// owner is in the nursery.
void* allocNurseryOrMallocBuffer(JS::Zone* zone, gc::Cell* owner,
size_t nbytes, arena_id_t arenaId);
- void* allocateBuffer(JS::Zone* zone, gc::Cell* owner, size_t nbytes);
+ void* allocateBuffer(JS::Zone* zone, gc::Cell* owner, size_t nbytes,
+ size_t maxNurserySize);
// Allocate a zero-initialized buffer for a given zone, using the nursery if
// possible. If the buffer isn't allocated in the nursery, the given arena is
@@ -178,7 +179,8 @@ class Nursery {
// Resize an existing buffer.
void* reallocateBuffer(JS::Zone* zone, gc::Cell* cell, void* oldBuffer,
- size_t oldBytes, size_t newBytes);
+ size_t oldBytes, size_t newBytes,
+ size_t maxNurserySize);
// Free an existing buffer.
void freeBuffer(JS::Zone* zone, gc::Cell* cell, void* buffer, size_t bytes);