Тёмный

Base [4]: Memory Management 

Mr. 4th Programming
Подписаться 6 тыс.
Просмотров 13 тыс.
50% 1

Setting up the code base's memory management strategy, with an allocator abstraction and a single block reserve and commit arena.

Опубликовано:

 

16 май 2021

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 30   
@maskisland2
@maskisland2 3 года назад
I never get tired of the little jingle.
@KennethBoneth
@KennethBoneth Год назад
You can see a good amount of influence from Casey Muratori (atleast it seems that way). Really cool to see how many people hand made hero has affected.
@luchong11
@luchong11 3 года назад
This is really nice. Are you developing this codebase for a specific project or just as a general boilerplate?
@Mr4thProgramming
@Mr4thProgramming 3 года назад
I have a few things I want to do and I want this codebase to sort of be my "base of operations". I want to do some creative work, generative and simulation art, some UI experiments, and some dev tools. No particular end goal, just exploring personal interests.
@LazySundayBook
@LazySundayBook 3 года назад
Do you think this memory management style works better for some class of software than others? i.e. "this is great for software in the application layer, but maybe not for supporting software in the platform layer"? or do you think this is the kind of memory style you'd use basically as your default for all projects?
@Mr4thProgramming
@Mr4thProgramming 3 года назад
There are certainly cases where I would play around with something else, however I find I can rely on this in pretty much any context for 99% of my memory management problems. It actually does a lot in the OS abstraction layer, which will be the second "arc" of videos.
@TJDierks
@TJDierks 3 года назад
Is there a particular reason you don't insert an Assert(result); at the end of the push procedure to catch the nullpointer immidiately? It seems that it might lead to some trouble if this "overflow" nullpointer happens in a piece of pretty complicated code or something, when the supposed reserved memory was pushed way earlier and not used at the time of allocation.
@Mr4thProgramming
@Mr4thProgramming 3 года назад
That is probably a good idea. I have a bit of overly-allergic attitude to asserts because I went through a phase of over using them to the point that they caused me more friction and confusion than benefits. In this case it would be good since I never want to handle a null return as a user anyways.
@joshuaworman4022
@joshuaworman4022 Месяц назад
just going to mention this music sounds sick at 2.75 speed.
@atomicgray
@atomicgray Месяц назад
Is it possible to reserve 1 tb ram ( virtual memory address space) size arena in cpp on windows system. With malloc i am limited by size of physical ram plus swap space. How to do it right. Edit: I was able to reserve 127 TB virtual address space with virtual alloc on windows 11 pro. I read the docs, the limit is 128 TB per user space process.
@Mr4thProgramming
@Mr4thProgramming Месяц назад
Yes it's possible on a 64-bit system. You would have to use VirtualAlloc the Windows specific API for virtual memory reservation. If I recall correctly, 1TB is pretty close to the maximum reserve size. You can only do it a couple of times before it will fail.
@atomicgray
@atomicgray Месяц назад
@@Mr4thProgramming I was able to reserve 127 TB virtual address space on windows 11 pro. I read the docs, the limit is 128 TB per user space process.
@staniw
@staniw 3 года назад
Great video. In your arena push/pop functions what is the idea behind aligning to M_COMMIT_BLOCK_SIZE -1? Seems to me like the -1 is superfluous and would cause the value to no longer be a power of 2.
@Mr4thProgramming
@Mr4thProgramming 3 года назад
I have no idea why I typed that! I think it's because when I use a Power-Of-2 number for rounding and I don't have the macro, the mechanism I use to get the rounding starts with subtracting 1, but the macro already does that and so this is just wrong. I'll put it on my notes to fix this in a cleanup, thanks!
@rafikrizik4722
@rafikrizik4722 2 года назад
Why set the M_DEFAULT_RESERVE_SIZE to be 1GB. I'm curious if this is a size that you mess with depending on the program you are importing these files in? I'm still learning, and I was looking at how you allocate for your global scratch memory. And it seemed like you allocated a size of 1GB for each of the 2 arena's you initialize in the function m_get_scratch(). Is this a size you have set and don't touch? If so, why 1GB Or, is this something you just placed initially, but depending on the program you are writing, you would change based on how large it was necessary for those global scratches to be?
@Mr4thProgramming
@Mr4thProgramming 2 года назад
Yeah I might tune it, but I'll try not to. I would like to avoid putting super big things on scratch arenas ever. If some particular thing is in the range of 1GB or even a few hundred MB it should probably get some special case low level handling anyways. Even if that just turns out to mean manually allocating an arena with a 4TB reserve or something.
@matias-eduardo
@matias-eduardo 3 года назад
Love it! So you're using M_Arena as a base for (eventually) multiple arenas (permanent, frame, temporary stack, etc.)? Labels: W = Written C = Committed R = Reserved F = Free So virtual memory would look like this? Multiple arenas: FF[WWCCCRRR]FFF[WWWCCCCRRRR]FF[WCCRRRR]FFF Same, but with arena names: FF[--Permanent--]FFF[------Frame----------]FF[---Temp----]FFF Would there be a case where you'd want it to look like this instead, where you have "sub-arenas", all in a single memory chunk? FFF[[WWCCC][WWWCCCC][WCC]RRRRRRRRR]FFFF FFF[[--Perm--][----Frame-----][Temp]RRRRRRRRR]FFFF One problem I see with one-big-chunk is you lose the ability to easily grow the sub-arenas as needed. Some benefits that come to mind is easy saving/addressing of entire app memory. Also, you might be able to get away with it if Temp is the only possibly-growing arena, and you can keep the others fixed. But I don't think this is always a possible or reliable assumption.
@Mr4thProgramming
@Mr4thProgramming 3 года назад
That's right. I'll use this primitive to get all kinds of memory lifetime "effects" in my code. I just tend to avoid subarenas. If I want a hierarchy of lifetime managers where every level of the hierarchy is equipped with a batch-free operation, I would have to do a bit more work than this. I will treat that as a special case solution and not the common allocator.
@phoebereader1191
@phoebereader1191 2 года назад
In the implementation of m_arena_push, why can you assume that the commit (base->commit) succeeds? VirtualAlloc, for example, can in theory fail on commit, AFAIK, even if you properly reserved the memory.
@Mr4thProgramming
@Mr4thProgramming 2 года назад
Strictly speaking you're right, we cannot assume that. With the amount of code I will write in this codebase, and the style I will use, it's very hard to avoid crashing if an allocation fails anyways. But if you look ahead to the recent upgrades video I handle this at least in the arena layer. The usage of the arena everywhere still assumes success, but the arena layer does the check now.
@phoebereader1191
@phoebereader1191 2 года назад
@@Mr4thProgramming makes sense, thanks for clarifying
@riomh
@riomh 3 года назад
How do you keep some parts of memory within this arena available for an arbitrarily lifetime? It's not clear to me since the arena is a stack. To stop using one chunk of memory, you'll also need to free everything in between that chunk and arena->pos. Expanding on that question, I am also confused about maintaining string immutability, since from what I can tell it's impossible to do with one stack arena. You can't pre-allocate the strings lower in the stack, since you don't necessarily know their size beforehand, but even if you did, you wouldn't be allowed to overwrite their immutable memory. Anyway, these are very interesting videos! Thanks for sharing :)
@Mr4thProgramming
@Mr4thProgramming 3 года назад
These are very good questions! You're asking questions like "How do you accomplish X?" and pointing out that "X" seems to be impossible because of the limitations of my systems. And my answer to both questions is just "I don't accomplish X." At least, not in the way you're thinking. You see if you're used to "playing by the rules" of malloc/free then you'd reasonably expect that I need to be able to free some memory when I'm done with it. Using the arena adds a new rule to the "game" which says "You are only allowed to free memory on the arena in the reverse order that you allocate it" which means I can't free something when I'm done with it unless I also free everything I've allocated since then too. Since you're not used to "playing the game" with this rule, you probably can think of tons of places where you write things in a certain way that would break the rule. So there would be some learning curve to you when you adopt this system and have to learn to play by this new rule. On the flip side, if you do learn to play by this new rule you make it possible to rely on the arena which is way *way* more efficient than malloc/free ever could be. The new constraint on one side of the system becomes freedom to use better solutions on the other side. What I have found after working with systems like this for a while is that almost everything can be written pretty naturally while following this rule, so it's just a matter of practice and training yourself to write code a different way. Also, we are missing one ingredient so far that really takes this system to the next level, and that's utilizing scratch arenas and multiple arenas to control different "lifetime-stacks". I'll explore the theme of scratch arenas a lot more in the second arc which should be out in July.
@riomh
@riomh 3 года назад
​@@Mr4thProgramming Thanks for your response! I understand how this sort of memory arena is really useful in immediate-mode scenarios where you can just push to the arena, do stuff with it, then pop afterwards. I have read through the 4coder custom source which does this. But I can't seem to get my head around how to manage the inevitable pieces of retained memory. For example, a 'last value' string that is used to check for changes in the contents of a different string every frame. This 'last value' string needs to persist between frames for an unknown duration (as its lifetime has no relation to the structure of the code) and potentially has substrings that reference it. Do you have any tips or resources for this sort of scenario? It has stumped me :P Glad to hear you're working on a second arc, looking forward to it!
@Mr4thProgramming
@Mr4thProgramming 3 года назад
@@riomh While you're learning how to work with a new system like this your challenge will be choosing between two competing ways of thinking. Do you (1) try to use the new system for everything, figure out how to take advantage of the system in the maximum way, and learn everything there is to know about the system? Or do you (2) use the best solution you already know for every problem in order to keep making progress in your project? In case you're in "#2 mode" my advice is you don't have to be a purist about arenas everywhere. I certainly use other patterns myself, in places where the best thing I know how to do is not compatible with the arena pattern. Now in "#1 mode" I do actually have an answer on how to do this, and sometimes it's a very useful option to have. The way to do it is to modify your idea of state. Instead of mutable state, each frame takes the old state and produces a new state. Pseudocode for this code transformation: from: "while(true){ update(&state); }" to: "while(true){ State next_state = update(state); state = next_state; }" What this means in Arena land is you have two arenas with lifetimes connected to frames. Only one of them is active in each frame, while the other is leftover from the previous frame. When "state" from the current frame needs to be "carried" into the next frame, you put it on your active frame arena. Between frames the arenas swap. You always start each frame with one empty arena, to fill up with state, and a full arena with state from the last frame.
@riomh
@riomh 3 года назад
@@Mr4thProgramming ​ Awesome, that makes sense. I suppose when working with simple arenas and immutable data a slightly more functional viewpoint can be useful, so I'll keep that in mind for future problems I encounter. I'm very much in the #1 mindset currently, trying to get a good handle on this style haha. Thanks so much for your time :)
@tomaszstanislawski457
@tomaszstanislawski457 2 месяца назад
+1 for typedef-ing function types rather than function pointer types
@poiu5302
@poiu5302 7 месяцев назад
I don't quite understand why you're introducing an additional step of commiting/decommiting the memory. Why would you want to add an imaginary capacity of memory that has already been allocated?
@Mr4thProgramming
@Mr4thProgramming 7 месяцев назад
It's a good question. Reserved memory is useful because it guarantees I can get the memory in a single contiguous block later. Imagine I have a task with an unknown number of small and bigger allocations interleaved in complicated ways. If I "reserve" a giant block that I know is more than enough for the whole task (say I reserve 4GB) the reserved memory can't be used by another allocator later, but it doesn't actually cost physical memory resources until it is committed. It may help to see the memory video from the OS series where I actually plug in the real low level reserve/commit features.
Далее
Base [5]: Strings
14:07
Просмотров 4,5 тыс.
Go 1.20 Memory Arenas Are AMAZING | Prime Reacts
16:38
Why does this Rust program leak memory?
35:24
Просмотров 57 тыс.
Arenas, strings and Scuffed Templates in C
12:28
Просмотров 80 тыс.
Dear Functional Bros
16:50
Просмотров 485 тыс.
WHY IS THE HEAP SO SLOW?
17:53
Просмотров 212 тыс.
I built my own 16-Bit CPU in Excel
16:28
Просмотров 1,4 млн
Dear Game Developers, Stop Messing This Up!
22:19
Просмотров 697 тыс.
One reason to Modify your Memory Allocator (C/C++)
10:23
Malware Development: Processes, Threads, and Handles
31:29