"uses virtual memory" - error in the `mem/virtual` docs?

In package mem/virtual - pkg.odin-lang.org, there are many comments about different procedures “using virtual memory” or not, which I think are misleading.

You can’t just opt into or out of using virtual memory - it’s determined by the OS/platform! Even “fixed” buffers use virtual memory implicitly. If they did not, that would mean your fixed buffer would have a physical (or linear) address instead of a virtual address, and you could break other programs. It’s not like you can tell the CPU “hey, don’t run this particular address in my program through the TLB or page walk it to get the virtual address - I got this.”

Running something that doesn’t “use virtual memory” is only possible for things like drivers or embedded devices with real-time OSes, all of which run in environments with virtual memory disabled at a platform level.

I think “uses virtual memory” could be replaced with “allocates from the virtual memory address space” and it would no longer be misleading. I get that “uses virtual memory” was probably intended to mean “uses VirtualAlloc() on Windows or mmap() on Linux”. However, for programmers that don’t quite have a sense yet of how a CPU or virtual memory works, it can reinforce an incorrect mental model.

As proof that this is confusing and misleading, even the author of the Odin Book (Fixed buffer arena - 13.3.3) says this:

Fixed buffer arenas are, like the static virtual memory arena, good for when you want to create an arena of a fixed size. The difference is that this one does not use virtual memory. Instead you can feed it any block of memory that you have laying around. (Yeah. It’s confusing that it is in core:mem/virtual when it does not use virtual memory.)

Let me know if you guys think I’m wrong. Thanks!

And as a follow up, it’s not clear if Karl thinks it’s confusing that arena_init_buffer() exists in core:mem/virtual if it could just exist in core:mem, or whether it’s confusing that arena_init_buffer() doesn’t use virtual memory (which it will, if the platform has virtual memory enabled).

So, here’s my answer why is arena_init_buffer() in core:mem/virtual (please ding me if I’m wrong):

Looking at Odin/core/mem/virtual/arena.odin at master · odin-lang/Odin · GitHub, the fixed buffer arena is built out of a Memory_Block from the given []byte (Memory_Block has committed and reserved, which only makes sense when the platform has virtual memory).

And if you think about it, if your platform doesn’t have virtual memory, why would you need to call arena_init_buffer() for anything? You don’t need to allocate from the virtual memory address space or set what is reserved or committed. You just use the buffer at the given address and move on with your day - you don’t need to init anything with the platform (you might zero initialize the data, but that’s it).

I suggest reading Odin/core/mem/virtual/doc.odin at master · odin-lang/Odin · GitHub if you haven’t already. It clearly mentions OSes that don’t support virtual memory, for example.

I believe the reason for arena_init_buffer() is to initialize the Arena struct, which contains all the book-keeping for the arena that the allocator created from arena_allocator() will use.

Arena
Arena :: struct {
	kind:                Arena_Kind,
	curr_block:          ^Memory_Block,

	total_used:          uint,
	total_reserved:      uint,

	default_commit_size: uint, // commit size <= reservation size
	minimum_block_size:  uint, // block size == total reservation

	temp_count:          uint,
	mutex:               sync.Mutex,
}
Usage
arena_buffer: virtual.Arena
buf: [2048 + size_of(virtual.Memory_Block)]byte // must add memory block size to get desired capacity
arena_buffer_err :=  virtual.arena_init_buffer(&arena_buffer, buf[:])
arena_buffer_allocator := virtual.arena_allocator(&arena_buffer)
defer virtual.arena_destroy(&arena_buffer)

temp := make([]byte, 42, arena_buffer_allocator) // test the arena allocator
fmt.println("Used:", arena_buffer.total_used, "Capacity:", arena_buffer.total_reserved)
free_all(arena_buffer_allocator)
fmt.println("Used:", arena_buffer.total_used, "Capacity:", arena_buffer.total_reserved)
1 Like

I think I get you now. Your problem is “uses” versus “allocates”. I guess you’re right, but I don’t think it’s that bad. The arena that just uses a buffer doesn’t care if that buffer is in virtual or physical address space. That’s why it doesn’t explicitly uses virtual memory. To me it works, but I get you (I think).

1 Like

Oops, I forgot that there is still an arena structure to initialize (it’s not just allocating a range of virtual addresses). Thanks for pointing that out.

So then the question remains: Why does arena_init_buffer() exist in core:mem/virtual when there already is arena_init() in core:mem?

I see that core:mem and core:mem/virtual have two separate Arena struct definitions - is the mem/virtual version just for interoperability with mem/virtual procedures, and for extra virtual memory-specific bounds checking?

(maybe there is a clue in the comments as well - perhaps the virtual mem version might allow for the possibility of advanced virtual memory-specific debugging techniques (like ‘poisoning’?))

This is my whole point - phrasing it like this makes it sound like virtual memory is some OS-level API or something that you opt into as a programmer - not a reality of the underlying platform running your code. It’s not possible to explicitly “use” or “not use” virtual memory at this level - you are using it whether you want to or not.

But also, even with a more ambiguous usage, if you look at how arena_init_buffer() is defined, it is touching “virtual memory” Memory_Blocks that have virtual memory-related fields (committed and reserved). So even in non-pedantic usage, arena_init_buffer() still “uses” virtual memory. It just doesn’t allocate it manually from the OS.

Thanks for humoring me on something that’s ultimately not that critical :slight_smile: It’s small things like this that bug me as a CPU guy and I think add to the confusion about the machine we program.

P.S.

The fact that I can just read the core library and more or less understand what’s going on with less than a month or two of using the language says a lot about why I like Odin vs. Rust or even Zig (I’ve heard stories of people who open the C++ std lib, but no one has lived to tell the tale).

1 Like

If I unterstand the docs correctly, the arena that is claimed to “use” virtual memory always uses virtual memory. If the OS does not have virtual memory, that arena will simply not work. I didn’t look, but I guess it won’t even compile for such a target. So while everything is using virtual memory on an OS that has it, this arena uses virtual memory by definition.