util: Avoid waste space when linear alloc'ing large sizes
authorCaio Oliveira <caio.oliveira@intel.com>
Tue, 3 Oct 2023 06:24:51 +0000 (23:24 -0700)
committerMarge Bot <emma+marge@anholt.net>
Sun, 8 Oct 2023 00:55:20 +0000 (00:55 +0000)
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>

src/util/ralloc.c
src/util/tests/linear_test.cpp

index 4dfc6f2..c85c61e 100644 (file)
@@ -1032,14 +1032,28 @@ linear_alloc_child(linear_ctx *ctx, unsigned size)
       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;
index 8fd95a3..2b57318 100644 (file)
@@ -53,3 +53,20 @@ TEST(LinearAlloc, RewriteTail)
 
    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);
+}