From: Rafael Antognolli Date: Fri, 7 Dec 2018 00:12:40 +0000 (-0800) Subject: anv/allocator: Rework chunk return to the state pool. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=7ed0898a8d43340011095586af3be53380bb084c;p=mesa.git anv/allocator: Rework chunk return to the state pool. 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 --- diff --git a/src/intel/vulkan/anv_allocator.c b/src/intel/vulkan/anv_allocator.c index ffac33e89d2..b6de2650abb 100644 --- a/src/intel/vulkan/anv_allocator.c +++ b/src/intel/vulkan/anv_allocator.c @@ -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; } }