Memory Arenas vs. Casting Memory

Hey there!

I've run into a problem in my own programming that requires an "arbitrary" (within some upper-bound) amount of memory. I rushed in to battle this beast, only to find self-doubt and reconsideration! At this point, I used Miblo's incredible episode guide to find an old Handmade Hero episode (34) to hear what the thought process of an expert was on this subject.

In this episode, Casey implements a very simple memory arena that makes use of the game's permanent storage. I have a question about this, though, and unfortunately I was not there to experience it in real-time!

To get to the point, my question is this: What are the benefits and drawbacks of say, the following...

1
2
3
4
5
6
7
8
// given some void *storage
struct MemoryForTask {
    u8 bytes_for_sub_task_1[megabytes(2)];
    u8 bytes_for_sub_task_2[megabytes(4)];
    u8 bytes_for_sub_task_3[megabytes(1)];
} *memory_for_task = (MemoryForTask *)storage;

// use memory_for_task->bytes_for_sub_task_n to allocate from and do stuff with


...and how do those compare to those of something similar to what Casey implemented?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
struct MemoryArena {
    u64 alloc_position;
    u64 memory_limit;
    u8 *memory;
};

void *alloc_from_arena(MemoryArena *arena, u64 number_of_bytes) {
    assert(arena->alloc_position + number_of_bytes <= arena->memory_limit);
    void *result = (void *)(arena->memory + arena->alloc_position);
    arena->alloc_position += number_of_bytes;
    return result;
}

// later, given some MemoryArena arena...

void *bytes_for_sub_task_1 = alloc_from_arena(&arena, megabytes(2));
void *bytes_for_sub_task_2 = alloc_from_arena(&arena, megabytes(4));
void *bytes_for_sub_task_3 = alloc_from_arena(&arena, megabytes(1));


I've tended to use the first system in the past, but I have a voice in my head suggesting I am not thinking of everything. Are there potential implications of the first system that could cause problems down the road, or are these two systems largely the same?

Thanks in advance for any responses!
Ryan

Edited by Ryan Fleury on Reason: Initial post
It's pretty much the same, you'll have no issues. Only disadvantage of first approach is that you cannot adjust size at runtime due to some condition. Sizes are compile-time constants.
A detail is that the arena method doesn't align memory (Casey later adds memory alignment). But that doesn't matter in your example case.
The arena method will assert if the arena doesn't have enough memory for the tasks. And you could use arenas for the sub tasks memory to also have the overflow assert if you allocate using alloc_from_arena.

If you need to keep the memory footprint of the application as low as possible, you could take advantage of the virtual memory by reserving a certain amount of memory, and only committing x megabytes. When you reach x megabytes, commit y megabytes more... But that's probably more suitable for "systems memory" than for "tasks memory".