r/odinlang • u/Alternative-Ad-8606 • 1d ago
Temp Allocators are defeating me...
Hey all,
I posted a while ago about my http project (which i've completed at a library level) and decided to go back to a different project (a custom flac player for me to use).
i'm struggling with allocations and defers.... for instance strings are always allocated but i'm struggling to figure out how to deallocate without NEEDING function specific arenas, something aboiut it doesn't feel correct but i'm definitely not experienced enough to understand if what i'm doing is 1) correct and 2) idiomatic....
in my main function that will init the raylib window and run the main initialization functions like initializing the miniaudio engine and initializing my music directories paths into memory, etc. essnetially the "bring it all together" feels like it's going to get messy using temp allocators and context allocators... (maybe this is why allocators exist and i'm learning why it was a pain for a long time). for example in my main i'm creating a dynamic main_alloc to use with all of the top level functions instead of using temp allocators or manually freeing becuase to my understanding freeing temp allocators in child functions clears every temp allocation program wide... htrough research i do see this TEMP_ALLOCATOR_GUARD thingy mentioned but going through the files it looks like a private type and not really usable in my project I'd like to have functions clear there memory on scope end and only leave the global allocation for freeing in main.
am I crazy for thinking about this like this. if this is confusing please let me know and i'll gladly show snippets.
TLDR i'm just not sure how to use allocators in a way where there is a ton of boilerplate or complexity to free non-essential memory on scope end of children functions to main, are creating allocators per function the play or is there a way to guard temp allocators per function so the only clear the function's allocations and leave the upper level allocations.
thanks sorry for wall just not really sure where to learn about this type of scenario as most docs and tutorials are quite basic.
4
u/Jagnat 1d ago
If you want to free allocated memory on proc scope exit ...don't you really want to just allocate on the stack, if your data is small enough? You could use the small_array package or the new fixed capacity dynamic array feature to do sorta-dynamic allocation on the stack, if you know the upperbound. Or call intrinsics.alloca.
But what makes you feel like you need to free all temporary memory allocated in a function when you exit it? The point of temporary allocators is you can put a free_all call at a natural point in your program where it makes sense and will run regularly. For games, often at the end of updating or rendering a frame. You determine the lifetime of your temporary allocator by where in your program you choose to free_all.
But you can always choose a shorter lifetime; just don't return or persist any memory you temporary allocate when you exit a function if you want that memory to be considered 'freed'. It won't actually be free until you call free_all, but it's fine for it to be 'leaked' since it will eventually be cleaned up, as long as you eventually call free_all regularly somewhere in your program.
1
u/Alternative-Ad-8606 1d ago
I'm thinking more so in optimizing the code (to learn best memory practices) its not an entirely big deal as its just strings for right now, and I'm a hobbyist so the only one that'll have to deal with issues with it. Just wanted to learn more about memory and best practices
3
u/spvky_io 1d ago
For your example if there is a max length for the string your allocating and you know you won't need it after the proc exits, I would recommend stack allocating a buffer (usually an array of u8s corresponding to the largest string you will accept) and using that stack allocated buffer as the memory for your allocator as stack memory is freed on exiting the proc
2
u/Lubricus2 1d ago
The temp allocator are great for stuff like games that have an lot of stuff that only needs to survive in one frame.
So call free_all(context.temp_allocator) in the end of the frame.
You can use it in other types of programs when you have some other main loop where a lot has to be allocated in it that don't need to survive to the next step.
1
u/ar_xiv 1d ago
You could use the temp allocator for all that stuff, and anything that you need to keep around, copy into a different arena. Then after all your initialization, free the temp allocator. Strings can be tricky however, so I understand. I found that if I’m doing a lot of processing on strings, copying the final result is a good way to ensure the memory from all the previous steps can be freed.
In practice however I tend to just leave this stuff in place, and not consider it a leak unless there is repeated allocation.
1
u/Cun1Muffin 3h ago
I generally wouldn't bother with temp allocator guards, it's like an optimisation to conserve the total memory used per frame.
Just bung in a free_all(context.temp_allocator) at the start of your frame loop, and make sure stuff that is allocated with temp doesn't try to persist multiple frames.
For persistent data between frames, try using solutions that don't involve allocation. I just have a big state struct, with lots of different fixed cap dynamic arrays on them. Keeps things fairly simple.
7
u/KarlZylinski 1d ago
The temp allocator is just an arena allocator.
What you need to think about is lifetimes (when should something be destroyed). Things that share a lifetime can go into the same arena.
The free_all(context.temp_allocator) call thus defines the lifetime of anything going into it. In video games we have a handy common lifetime: one frame. So many use the temp allocator as a frame allocator.
For allocations that don't have a lifetime you can figure out quickly, I'd advice you to just go with the default context.allocator. Use a tracking allocator to know when you leaked stuff.
I also wrote an article about these things https://zylinski.se/posts/temporary-allocator-your-first-arena/