Two questions regarding VirtualAlloc (Windows) and mmap (Linux)

Started by
14 comments, last by EBZ 3 years, 7 months ago

I've been learning how to allocate chunks of memory. I've learned the windows way:

unsigned char* memory = (unsigned char*)VirtualAlloc(0, memorySize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

But for Linux I had no idea how to do it. A little bit of searching led me to mmap. I'm trying to do the same thing I did with VirtualAlloc, but with mmap. I'm still confused and not sure if I'm doing it right:

unsigned char* memory = (unsigned char*) mmap(0, memory->memorySize, PROT_READ | PROT_WRITE, MAP_PRIVATE, 0, 0);


Question about how to handle game objects and allocated memory

Let's say I allocate 1 gig of memory using VirtualAlloc (or mmap). Now I want to grab a small chunk of that memory using this:

uint8* AllocateMemory(Memory_Allocator* allocator, uint64 size) {
    assert(allocator->bytes_remaining >= size);
    uint8* mem = allocator->next;
    allocator->next += size;
    return mem;
}

I use the function above to allocate N amount of bytes by doing the following:

Player* players = (Player*)AllocateMemory(sizeof(Player) * numPlayers);  

From there I can for loop players and do everything I need to do.

Is this a good way of approaching managing memory in game development? At least to get started, is there anything I should I look out for?

Hope this all makes sense, and thanks!

Advertisement

What problem are you trying to solve that malloc/free, new/delete, etc. does not?

From the casts I assume you are also using C++ not C, so you probably want constructors/destructors to work, including for arrays, so if you do want to create your own allocator there is some more work to do there.

The AllocateMemory you have there is not suitable as a general purpose allocator as you have no way to free memory. But since the actual allocation can be very fast, it can be made into something more serviceable if used for a specific purpose. For example you could allocate all the space needed to load static level/map data, then discard it all when the level is complete. Or for startup data.


Especially in C this can actually also make the memory management work a bit easier for objects that don't need any cleanup/destructor (e.g. files, network handles, etc.), as you don't need to manually free them individually. And you can easily enough extend it to handle cleanup automatically as well, but C++ has RAII for many of these things.

The assert is particularly dangerous though, you need to be 100% sure there is no possible way a player can do anything to exceed the memory allocation, since in a release build best case you will get an immediate crash, worse case it will go and corrupt whatever memory page happened to be the next address. A single if statement is extremely cheap on any modern system (you could also maybe do something with guard pages).

And if you can practically know how much memory will be needed and for what purpose ahead of time can vary a lot by game and what gameplay limitations you impose across a range of hardware on the PC platform.

Hello SyncViews, thanks for your reply! I'm not really trying to solve anything, just learning from a few videos here and there.
I actually started using VirtualAlloc because of a HandMadeHero video.

Let me see if I understand, if I'm going to allocate a chunk of memory like I did with VirtualAlloc or use malloc as you suggested: I should use it for level data, replay data, maybe game resources? But in terms of storing the current entities in the game, I should avoid storing it there? Not sure if I should have my chunk of memory allocated and a vector with my current scene entities.

Another quick question if you don't mind, what's the difference between VirtualAlloc and malloc? If I didn't read you wrong, I could do the same thing I did with VirtualAlloc but with malloc. I guess I'm wondering since I've seen VirtualAlloc being used in a couple tutorials.

Btw, thanks for letting me know about the assert, I'll 100% fix that. Hope I made sense in those last questions. Hey thanks again!

EBZ said:
Another quick question if you don't mind, what's the difference between VirtualAlloc and malloc? If I didn't read you wrong, I could do the same thing I did with VirtualAlloc but with malloc. I guess I'm wondering since I've seen VirtualAlloc being used in a couple tutorials.

malloc is part of the c(++) standard library and available on all platforms. VirtualAlloc is windows-specific and only one of many special-purpose/OS-specific allocation functions (some other including HeapAlloc). The specific functions obviously can do more (by being able to specifiy flags for targeting certain areas of memory, as well as usage-scenarios, but malloc on the other hand is available on every OS.

If you are targeting a very specific use-case, then having VirtualAlloc with the right flags might be the right thing do have; with an appropriate abstraction for calling the correct function on linux. If you are making a general-purpose allocator on the other hand you might as well request the memory with malloc. Other than not being able to specify flags, there is usally no magic going on with eigther malloc or VirtualAlloc or any other variant, its probably all going to end up being implemented similarily and ending up calling the same OS-functions down the line.

EBZ said:
Let me see if I understand, if I'm going to allocate a chunk of memory like I did with VirtualAlloc or use malloc as you suggested: I should use it for level data, replay data, maybe game resources?

Personally, I wouldn't bother too much with some clever overarcing memory-allocation-scheme. I personally don't use any general-purpose allocators in my engine, I only do pooling for very specific use-cases (temporay render-data, sprite-batching, bytecode-generation, …), and thats usually as simple as requesting a std::vector<char>/std::unique_ptr<char[]> and emplace-newing the data into it. Outside of that I simply try to stay on the stack most of the time, use std::array<Type>/std::vector<Type> where possible.
I might be mistaken, but from my own tests overhead for regular heap-allocations have become way smaller over time (even though memory-access is now a main bottleneck). There are some examples, even from this site that compare heap vs custom-allocation, which originally showed 8x-32x speedups, and when you run it now its more like 2x-4x at best. Sure, thats still something, but IMHO not worth going through all the trouble (in my own use-case I don't care that much if the startup-time of my editor went from 0.8s to 0.4s that would make the trouble worth it). But if you really do, it would probably be best just to drop in something like jemalloc, which globally overrides new/delete (more specifically malloc/free), and pretty much gives you the same performance-gains then you would have from doing it manually if you are really trying to squeeze everything out of it. Just my two cents though, and I'm sure people are going to disagree based on how many people are doing general-purpose allocators.

Hey Julien thanks for the reply ? So I guess after reading your and SyncView's replies, I'm probably overdoing things here.

So all you do is just use vectors to store your game objects? Maybe I can just use malloc to allocate memory for reply data for example?

Is this kind of what you're doing?


int main () {
    std::vector<Player> players;
    std::vector<Mesh> meshes;

    while (true) {
        for (size_t i = 0; i < players.size(); i += 1) {
            // Update Players
        }
    }
}

So all you do is just use vectors to store your game objects? Maybe I can just use malloc to allocate memory for reply data for example?

Is this kind of what you're doing?

Yeah, pretty much!
If you want to be able to hold on to Player/Mesh-pointer somewhere, you would go for storing std::vector<std::unique_ptr<Player>>, which would be slower but should still be acceptable most of the time (and on the other hand you get the advantage of being able to hold on to pointers which could save you performance on the other hand if you had to continously access an instance of “Player” from the continous vector.

I personally go for the contious-vector approach, which gives nice speed for iteration but complicates things a little. I can never hold on to a pointer to a component, which doesn't matter to me since most “scripting” is done with my visual-interface where entirely hidden away and could not/need not be hold on via pointer as you would need to do in “normal” code. But I also only did that as part of an optimization for a class-project, my original approach was to have a std::vector<std::unique_ptr<Component>>, and well, the gains in speed by going from that to the continous approach wasn't even that great. So do whatever fits best for your needs.

In C, it's malloc and free.

In C++, it's new and delete, or new[] and delete[].

I have 75 repositories on GitHub, and I use the new/delete keywords… once. You seriously don't need them in 99% of the cases. I ended up using them alongside the multithreading portion of C++, and the only reason I used new is so that I can force quit a thread by deleting it. Even then, I don't know if that was such a good idea.

Also, don't forget about the rest of the Standard Template Library. It has a map, a set, a list (both singly and doubly-linked), a double-ended queue, and on and on.

One thing useful about the vector container is that it supplies a pointer to memory that can be used for any C-style buffer. Check out the data() member, or consider using &my_vector[0], which is literally a pointer that points to the address of the first element in the vector. This is allowed because the vector ensures that all of its data are in one, contiguous block of memory, just like a C-style buffer would.

Cool stuff.

EBZ said:
So all you do is just use vectors to store your game objects?

It depends on how frequently allocations may happen during gameplay.
If you can allocate it all during loading and don't need new things at runtime, using std::vector for everything is fine, convenient and saves time.
If you constantly delete and create objects, e.g. open world RPG, allocations might become frequent and a performance problem. Then you need some memory management to do all this while still working with constant memory for most (which does not rule out using std::vector).

I have game pieces, and I store them in a list, because they get erased all the time. In a list, the erasure costs a constant amount of time — O(1) time complexity.

When you do an erasure in the middle of a vector, the time complexity is like O(n/2)… much larger, where n is the number of elements in the vector.

There is a reason why both containers exist in the STL. ?

taby said:

I have game pieces, and I store them in a list, because they get erased all the time. In a list, the erasure costs a constant amount of time — O(1) time complexity.

When you do an erasure in the middle of a vector, the time complexity is like O(n/2)… much larger, where n is the number of elements in the vector.

There is a reason why both containers exist in the STL. ?

How many elements do you really have? Even with the O(1) vs O(n) speed, vector will outperform list for deletions for a quite large n, I'd say to get a benefit from list you'd need easily > 100 elements due to the lists fragmentation. This is especially true if you have to search the list for the element that you want to delete, so unless you have the iterator around already thats one more point for not using the list over vector.
For the same reason I made a small_map-class, which is just a vector with a map-like interface (internally using linear search), because it outperforms map/unordered_map until you get to very large element-counts.

This topic is closed to new replies.

Advertisement