Last week was my birthday (all week), and I decided to gift myself fewer blog posts.
Now that things are back to normal, however, I’ll be easing my way back into the blogosphere. Today I’m going to be looking briefly at the benefits of code from a recent MR by Erik Faye-Lund which improves memory allocation in
ntv to let us better track all the allocations done in the course of doing compiler stuff, with the key benefit being that we’re no longer leaking this entire thing every time we compile a shader (oops).
Mesa internals use an allocator called
ralloc, and this was first added to the tree by Kenneth Graunke way back in 7.10.1, or at least that was the earliest reference of it I could find. It’s used everywhere, and it has a number of nice features that justify its use other than simple efficiency.
For this post, I’ll be looking specifically at its memory context capability.
Typically in C, we have our calls to
malloc, and we have to track all these allocations, ensuring ownership is preserved, and then later call
free at an appropriate time on everything that we’ve allocated.
Not so with the magic of
A call to
ralloc_context() creates a new memory context. Subsequent allocations for related objects requiring allocation can then pass this context to the related
ralloc_size() calls, and this allocation will then be added to the specified context. At a later point, the context can be freed, taking all the allocated memory with it.
This enables code like:
void *ctx = ralloc_context(NULL); void *some_mem1 = ralloc_size(ctx, some_size1); void *some_mem2 = ralloc_size(ctx, some_size2); void *some_mem3 = ralloc_size(ctx, some_size3); ralloc_free(ctx);
which will free all allocations created using
But wait, there’s more! Every allocation made using
ralloc is implicitly a context like this, which means that any time a struct is created with allocated members, the struct can be passed as a context, meaning that a destructor function for that struct can simply be written as a call to
ralloc_free() which passes the struct, and newly-added members don’t need to be deallocated.
So for example:
struct somestruct *s = ralloc_size(NULL, sizeof(struct somestruct)); s->member1 = ralloc_size(s, sizeof(member1_size)); ralloc_free(s);
will free the struct and
member, but also adding e.g.,:
s->member2 = ralloc_size(s, sizeof(member2_size));
for a new member at some point requires no changes to the deallocation codepath.
At some point, it’s likely we’ll be going through the zink codebase to add more
ralloc usage, both for helping to avoid memory leaks and for the other more general benefits that memory allocators bring over our existing usage of raw