[Mesa-dev] [RFC PATCH 12/14] anv/allocator: Rework chunk return to the state pool.

Rafael Antognolli rafael.antognolli at intel.com
Tue Dec 11 23:31:13 UTC 2018


On Mon, Dec 10, 2018 at 11:10:02PM -0600, Jason Ekstrand wrote:
> 
> 
> On Mon, Dec 10, 2018 at 5:48 PM Rafael Antognolli <rafael.antognolli at intel.com>
> wrote:
> 
>     On Mon, Dec 10, 2018 at 04:56:40PM -0600, Jason Ekstrand wrote:
>     > On Fri, Dec 7, 2018 at 6:06 PM Rafael Antognolli <
>     rafael.antognolli at intel.com>
>     > wrote:
>     >
>     >     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.
>     >     ---
>     >      src/intel/vulkan/anv_allocator.c | 147
>     +++++++++++++++++++++----------
>     >      1 file changed, 102 insertions(+), 45 deletions(-)
>     >
>     >     diff --git a/src/intel/vulkan/anv_allocator.c b/src/intel/vulkan/
>     >     anv_allocator.c
>     >     index 31258e38635..bddeb4a0fbd 100644
>     >     --- a/src/intel/vulkan/anv_allocator.c
>     >     +++ b/src/intel/vulkan/anv_allocator.c
>     >     @@ -994,6 +994,97 @@ anv_state_pool_get_bucket_size(uint32_t bucket)
>     >         return 1 << size_log2;
>     >      }
>     >
>     >     +/** Helper to create a chunk into the state table.
>     >     + *
>     >     + * It just creates 'count' entries into the state table and update
>     their
>     >     sizes,
>     >     + * offsets and maps, also pushing them as "free" states.
>     >     + */
>     >     +static void
>     >     +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;
>     >     +
>     >     +   uint32_t st_idx = anv_state_table_add(&pool->table, count);
>     >     +   for (int i = 0; i < count; i++) {
>     >     +      /* update states that were added back to the state table */
>     >     +      struct anv_state *state_i = anv_state_table_get(&pool->table,
>     >     +                                                      st_idx + i);
>     >     +      state_i->alloc_size = block_size;
>     >     +      state_i->offset = chunk_offset + block_size * i;
>     >     +      struct anv_pool_map pool_map = anv_block_pool_map(&pool->
>     block_pool,
>     >     +                                                        state_i->
>     offset);
>     >     +      state_i->map = pool_map.map + pool_map.offset;
>     >     +   }
>     >     +
>     >     +   uint32_t block_bucket = anv_state_pool_get_bucket(block_size);
>     >     +   anv_state_table_push(&pool->buckets[block_bucket].free_list,
>     >     +                        &pool->table, st_idx, count);
>     >     +}
>     >     +
>     >     +static uint32_t
>     >     +calculate_divisor(uint32_t size)
>     >     +{
>     >     +   uint32_t bucket = anv_state_pool_get_bucket(size);
>     >     +
>     >     +   while (bucket >= 0) {
>     >     +      uint32_t bucket_size = anv_state_pool_get_bucket_size(bucket);
>     >     +      if (size % bucket_size == 0)
>     >     +         return bucket_size;
>     >     +   }
>     >     +
>     >     +   return 0;
>     >     +}
>     >     +
>     >     +/** Returns a chunk of memory back to the state pool.
>     >     + *
>     >     + * If small_size is zero, we split chunk_size into pool->
>     block_size'd
>     >     pieces,
>     >     + * and return those. If there's some remaining 'rest' space
>     (chunk_size is
>     >     not
>     >     + * divisble by pool->block_size), then we find a bucket size that is
>     a
>     >     divisor
>     >     + * of that rest, and split the 'rest' into that size, returning it
>     to the
>     >     pool.
>     >     + *
>     >     + * If small_size is non-zero, we use it in two different ways:
>     >     + *    * if it is larger than pool->block_size, we split the chunk
>     into
>     >     + *    small_size'd pieces, instead of pool->block_size'd ones.
>     >     + *    * we also use it as the desired size to split the 'rest' after
>     we
>     >     split
>     >     + *    the bigger size of the chunk into pool->block_size;
>     >
>     >
>     > This seems both overly complicated and not really what we want.  If I
>     have a
>     > block size of 8k and allocate a single 64-byte state and then a 8k state,
>     it
>     > will break my almost 8k of padding into 511 64-byte states and return
>     those
>     > which may be very wasteful if the next thing I do is allocate a 1K state.
> 
>     Good, this would definitely be a waste.
> 
>     > It also doesn't provide the current alignment guarantees that all states
>     are
>     > aligned to their size.  While the alignment guarantee doesn't matter for
>     most
>     > large states, it does matter for some of the smaller sizes.  Now that I
>     look at
>     > it in detail, it appears that the highest alignment we ever require is
>     64B and
>     > the smallest size we allow is 64B so maybe it just doesn't matter?
>     >
>     > Assuming we don't care about alignments, we could do something like this?
>     >
>     > if (small_size > 0) {
>     >    assert(chunk_size % small_size == 0);
>     >    anv_state_pool_return_blocks(pool, chunk_offset, chunk_size /
>     small_size,
>     > small_size);
>     > } else {
>     >    uint32_t divisor = MAX_STATE_SIZE;
>     >    while (divisor >= MIN_STATE_SIZE) {
>     >       uint32_t nblocks = chunk_size / divisor;
>     >       if (nblocks > 0) {
>     >          anv_state_pool_return_blocs(pool, chunk_offset, nblocks,
>     divisor);
>     >          chunk_offset += nblocks * divisor;
>     >          chunk_size -= nblocks * divisor;
>     >       }
>     >    }
>     > }
> 
>     The code above is indeed way simpler and it does seem to achieve
>     what we want for the "return padding" case.
> 
>     However, we also use this helper for returning blocks that we got from
>     the freelist, but were only partially used. For instance if we need to
>     allocate a state of size 64 bytes, and we have a block of 8KB in the
>     freelist, due to the snippet above (small_size == 64) we will end up
>     splitting it into 511 64-byte states too.
> 
>     Maybe (if we want to keep the old semantics), in the case where
>     small_size > 0, we have to do something like:
> 
>     if (small_size > 0) {
>        assert(chunk_size % small_size == 0);
>        if (chunk_size > pool->block_size) {
>           assert(chunk_size % pool->block_size == 0);
>           uint32_t nblocks = chunk_size / pool->block_size - 1;
>           anv_state_pool_return_blocks(pool, offset, nblocks, pool->
>     block_size);
>           chunk_size -= nblocks * pool->block_size;
>           offset += nblocks * pool->block_size;
>        }
>        anv_state_pool_return_blocks(pool, chunk_offset, chunk_size /
>     small_size, small_size);
>     } else {
>        ... your else clause here
>     }
> 
>     Maybe it's over complicating things again... what do you think?
> 
> 
> Maybe?  Or maybe we want to just keep the block_size semantics for everything
> and split padding into some small chunks as needed and then a bunch of
> block_size chunks.  If we did that, we'd have the same semantics for
> everything.

Just to be sure, how does something like this look:

 uint32_t divisor = MAX2(pool->block_size, small_size);
 uint32_t nblocks = chunk_size / divisor;
 uint32_t rest = chunk_size - nblocks * divisor;

 /* First return pool->block_size'd chunks.*/
 uint32_t offset = chunk_offset + rest;
 anv_state_pool_return_blocks(pool, offset, nblocks, divisor);

 chunk_size = rest;

 uint32_t b = anv_state_pool_get_bucket(divisor);
 while (chunk_size > 0 && b >= ANV_MIN_STATE_SIZE_LOG2) {
    divisor = anv_state_pool_get_bucket_size(b);
    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;
    }
    b--;
 }

I'm also wondering if it's worth leaving the "small_size" portion of it,
and if so I probably need a better name for it.

> 

<snip>

> 
> 
> I think what I'd recommend is that you pull the first helper in this patch into
> it's own patch right before patch 2 and use it replace the two in-line copies
> here.  Then the API wouldn't look nearly as horrible and this could focus on
> the heuristic for dealing with padding (and maybe even be rolled into the
> padding patch).

Cool, done locally already, thanks!

> 
>     >     -            chunk_size = pool->block_size;
>     >     -         }
>     >     -
>     >     -         assert(chunk_size % alloc_size == 0);
>     >     -         uint32_t push_back = (chunk_size / alloc_size) - 1;
>     >     -         uint32_t st_idx = anv_state_table_add(&pool->table,
>     push_back);
>     >     -         for (int i = 0; i < push_back; i++) {
>     >     -            /* update states that were added back to the state table
>     */
>     >     -            struct anv_state *state_i = anv_state_table_get(&pool->
>     table,
>     >     -                                                            st_idx +
>     i);
>     >     -            state_i->alloc_size = alloc_size;
>     >     -            state_i->offset = chunk_offset + alloc_size * (i + 1);
>     >     -            struct anv_pool_map pool_map = anv_block_pool_map(&
>     pool->
>     >     block_pool,
>     >     -                                                             
>     state_i->
>     >     offset);
>     >     -            state_i->map = pool_map.map + pool_map.offset;
>     >     -         }
>     >     -         anv_state_table_push(&pool->buckets[bucket].free_list,
>     >     -                              &pool->table, st_idx, push_back);
>     >     -
>     >     -         offset = chunk_offset;
>     >     +         uint32_t return_offset = chunk_offset + alloc_size;
>     >     +         uint32_t return_size = chunk_size - alloc_size;
>     >     +         anv_state_pool_return_chunk(pool, return_offset,
>     >     +                                     return_size, alloc_size);
>     >               goto done;
>     >            }
>     >         }
>     >     --
>     >     2.17.1
>     >
>     >     _______________________________________________
>     >     mesa-dev mailing list
>     >     mesa-dev at lists.freedesktop.org
>     >     https://lists.freedesktop.org/mailman/listinfo/mesa-dev
>     >
> 


More information about the mesa-dev mailing list