anv/allocator: Rework chunk return to the state pool.
authorRafael Antognolli <rafael.antognolli@intel.com>
Fri, 7 Dec 2018 00:12:40 +0000 (16:12 -0800)
committerRafael Antognolli <rafael.antognolli@intel.com>
Thu, 17 Jan 2019 23:08:17 +0000 (15:08 -0800)
This commit tries to rework the code that split and returns chunks back
to the state pool, while still keeping the same logic.

The original code would get a chunk larger than we need and split it
into pool->block_size. Then it would return all but the first one, and
would split that first one into alloc_size chunks. Then it would keep
the first one (for the allocation), and return the others back to the
pool.

The new anv_state_pool_return_chunk() function will take a chunk (with
the alloc_size part removed), and a small_size hint. It then splits that
chunk into pool->block_size'd chunks, and if there's some space still
left, split that into small_size chunks. small_size in this case is the
same size as alloc_size.

The idea is to keep the same logic, but make it in a way we can reuse it
to return other chunks to the pool when we are growing the buffer.

v2:
 - Include Jason's suggestions to the algorithm that returns chunks.
 - Update comments.

v3:
 - Disallow returning 0 blocks (Jason).
 - fix min_size in the loop (Jason).
 - remove temporary variables (Jason)
v4:
 - return_chunk() should never return blocks larger than
 pool->block_size.

Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
src/intel/vulkan/anv_allocator.c

index ffac33e89d2b11c591951e1713625db9781c3fdb..b6de2650abba289517c5d4f5539bb5d33484137d 100644 (file)
@@ -951,8 +951,8 @@ anv_state_pool_return_blocks(struct anv_state_pool *pool,
                              uint32_t chunk_offset, uint32_t count,
                              uint32_t block_size)
 {
-   if (count == 0)
-      return;
+   /* Disallow returning 0 chunks */
+   assert(count != 0);
 
    /* Make sure we always return chunks aligned to the block_size */
    assert(chunk_offset % block_size == 0);
@@ -974,6 +974,58 @@ anv_state_pool_return_blocks(struct anv_state_pool *pool,
                       &pool->table, st_idx, count);
 }
 
+/** Returns a chunk of memory back to the state pool.
+ *
+ * Do a two-level split. If chunk_size is bigger than divisor
+ * (pool->block_size), we return as many divisor sized blocks as we can, from
+ * the end of the chunk.
+ *
+ * The remaining is then split into smaller blocks (starting at small_size if
+ * it is non-zero), with larger blocks always being taken from the end of the
+ * chunk.
+ */
+static void
+anv_state_pool_return_chunk(struct anv_state_pool *pool,
+                            uint32_t chunk_offset, uint32_t chunk_size,
+                            uint32_t small_size)
+{
+   uint32_t divisor = pool->block_size;
+   uint32_t nblocks = chunk_size / divisor;
+   uint32_t rest = chunk_size - nblocks * divisor;
+
+   if (nblocks > 0) {
+      /* First return divisor aligned and sized chunks. We start returning
+       * larger blocks from the end fo the chunk, since they should already be
+       * aligned to divisor. Also anv_state_pool_return_blocks() only accepts
+       * aligned chunks.
+       */
+      uint32_t offset = chunk_offset + rest;
+      anv_state_pool_return_blocks(pool, offset, nblocks, divisor);
+   }
+
+   chunk_size = rest;
+   divisor /= 2;
+
+   if (small_size > 0 && small_size < divisor)
+      divisor = small_size;
+
+   uint32_t min_size = 1 << ANV_MIN_STATE_SIZE_LOG2;
+
+   /* Just as before, return larger divisor aligned blocks from the end of the
+    * chunk first.
+    */
+   while (chunk_size > 0 && divisor >= min_size) {
+      nblocks = chunk_size / divisor;
+      rest = chunk_size - nblocks * divisor;
+      if (nblocks > 0) {
+         anv_state_pool_return_blocks(pool, chunk_offset + rest,
+                                      nblocks, divisor);
+         chunk_size = rest;
+      }
+      divisor /= 2;
+   }
+}
+
 static struct anv_state
 anv_state_pool_alloc_no_vg(struct anv_state_pool *pool,
                            uint32_t size, uint32_t align)
@@ -1004,7 +1056,9 @@ anv_state_pool_alloc_no_vg(struct anv_state_pool *pool,
           */
          state->alloc_size = alloc_size;
 
-         /* We've found a chunk that's larger than the requested state size.
+         /* Now return the unused part of the chunk back to the pool as free
+          * blocks
+          *
           * There are a couple of options as to what we do with it:
           *
           *    1) We could fully split the chunk into state.alloc_size sized
@@ -1026,28 +1080,15 @@ anv_state_pool_alloc_no_vg(struct anv_state_pool *pool,
           *       two-level split.  If it's bigger than some fixed block_size,
           *       we split it into block_size sized chunks and return all but
           *       one of them.  Then we split what remains into
-          *       state.alloc_size sized chunks and return all but one.
+          *       state.alloc_size sized chunks and return them.
           *
-          * We choose option (3).
+          * We choose something close to option (3), which is implemented with
+          * anv_state_pool_return_chunk(). That is done by returning the
+          * remaining of the chunk, with alloc_size as a hint of the size that
+          * we want the smaller chunk split into.
           */
-         if (chunk_size > pool->block_size &&
-             alloc_size < pool->block_size) {
-            assert(chunk_size % pool->block_size == 0);
-            /* We don't want to split giant chunks into tiny chunks.  Instead,
-             * break anything bigger than a block into block-sized chunks and
-             * then break it down into bucket-sized chunks from there.  Return
-             * all but the first block of the chunk to the block bucket.
-             */
-            uint32_t push_back = (chunk_size / pool->block_size) - 1;
-            anv_state_pool_return_blocks(pool, chunk_offset + pool->block_size,
-                                         push_back, pool->block_size);
-            chunk_size = pool->block_size;
-         }
-
-         assert(chunk_size % alloc_size == 0);
-         uint32_t push_back = (chunk_size / alloc_size) - 1;
-         anv_state_pool_return_blocks(pool, chunk_offset + alloc_size,
-                                      push_back, alloc_size);
+         anv_state_pool_return_chunk(pool, chunk_offset + alloc_size,
+                                     chunk_size - alloc_size, alloc_size);
          goto done;
       }
    }