In the linear allocator, when a size larger than the minimum
buffer size is allocated, we currently create the new buffer
to fit exactly the requested size.
In that case, don't bother updating the `latest` pointer, since
this newly created buffer is already full. In the worst case,
the current `latest` is full and it would be the same; in the
best case, there's still room for small allocations, so we avoid
wasting space if the next allocations would fit.
Reviewed-by: Emma Anholt <emma@anholt.net>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25517>
if (unlikely(!ptr))
return NULL;
- ctx->offset = 0;
- ctx->size = node_size;
- ctx->latest = ptr + canary_size;
#ifndef NDEBUG
- linear_node_canary *canary = get_node_canary(ctx->latest);
+ linear_node_canary *canary = (void *) ptr;
canary->magic = LMAGIC_NODE;
canary->offset = 0;
#endif
+
+ /* If the new buffer is going to be full, don't update `latest`
+ * pointer. Either the current one is also full, so doesn't
+ * matter, or the current one is not full, so there's still chance
+ * to use that space.
+ */
+ if (unlikely(size == node_size)) {
+#ifndef NDEBUG
+ canary->offset = size;
+#endif
+ assert((uintptr_t)(ptr + canary_size) % SUBALLOC_ALIGNMENT == 0);
+ return ptr + canary_size;
+ }
+
+ ctx->offset = 0;
+ ctx->size = node_size;
+ ctx->latest = ptr + canary_size;
}
void *ptr = (char *)ctx->latest + ctx->offset;
ralloc_free(ctx);
}
+
+TEST(LinearAlloc, AvoidWasteAfterLargeAlloc)
+{
+ void *ctx = ralloc_context(NULL);
+ linear_ctx *lin_ctx = linear_context(ctx);
+
+ char *first = (char *) linear_alloc_child(lin_ctx, 32);
+
+ /* Large allocation that would force a larger buffer. */
+ linear_alloc_child(lin_ctx, 1024 * 16);
+
+ char *second = (char *) linear_alloc_child(lin_ctx, 32);
+
+ EXPECT_EQ(second - first, 32);
+
+ ralloc_free(ctx);
+}