6

I need to allocate large regions of memory (megabytes) with large alignments (also potentially in the megabyte range). The VirtualAlloc family of functions don't seem to provide options to do this.

What I do on Linux to achieve this is to mmap a larger region - large enough to guarantee that a sufficiently large region with the required alignment will be contained in it - and then munmap the regions at the beginning and the end of the large region that are not needed.

As an example, let's say I need 4 megabytes, aligned on a 1 megabyte boundary (i.e. the start of the region having zeroes in the lowest 20 bits). I'd mmap 5 megabytes. Let's say I get the region 0x44ff000-0x49ff000. Within that region is contained the region 0x4500000-0x4900000, which is aligned on a 1 megabyte boundary. I would then munmap 0x44ff000-0x4500000 and 0x4900000-0x49ff000.

Can I do something similar on Win32? If I use VirtualProtect with PAGE_NOACCESS, will the memory be freed? Is there a better solution?

Mark Probst
  • 7,107
  • 7
  • 40
  • 42
  • Having an alignment requirement for *virtual* memory is very strange, this only tends to matter for physical memory. Anyhoo, no can do on Windows. But it doesn't matter because you only have to *reserve* allocations. Committing is a separate step. Requesting an allocation on a specific happy address is also possible. Use VirtualAlloc(). – Hans Passant Sep 30 '11 at 23:43

3 Answers3

8

Yes, you can use the same technique. VirtualAlloc a large range as MEM_RESERVE. Find the sub-range that is appropriately aligned and call VirtualAlloc a second time on the sub-range with MEM_COMMIT.

Raymond Chen
  • 44,448
  • 11
  • 96
  • 135
  • Is it possible to release unused portion of the range? (similar to `munmap()` calls mentioned in original post) According to my reading of `VirtualFree()` docs -- it isn't possible... – C.M. Feb 15 '23 at 19:57
  • @C.M. `VirtualFree` can be used with `MEM_DECOMMIT` to undo the effect of an earlier `VirtualAlloc` with `MEM_COMMIT`. You can decommit part of a previous commit. – Raymond Chen Feb 15 '23 at 20:07
  • yes, but you can't release unused portion of reserved range, right? `MEM_RELEASE` demands `dwSize` to be `0` – C.M. Feb 15 '23 at 20:12
  • @C.M. Right, you can decommit individual pages, but you cannot unreserve individual pages. – Raymond Chen Feb 15 '23 at 20:14
1

Starting with Windows 10 version 1803 (RS4, build 17134) and Server 2019 you can use VirtualAlloc2 and pass MEM_ADDRESS_REQUIREMENTS with required alignment set.

Example:

MEM_ADDRESS_REQUIREMENTS requirement;
requirement.LowestStartingAddress = NULL;
requirement.HighestEndingAddress = NULL;
requirement.Alignment = 1024 * 1024 * 1024; // align to 1GB boundary

MEM_EXTENDED_PARAMETER extended;
extended.Type = MemExtendedParameterAddressRequirements;
extended.Pointer = &requirement;

VirtualAlloc2 (NULL, NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, &extended, 1);
0

Have a look at the source for _aligned_malloc in the windows/MSVC crt, its very simple to use the same method to align virtual memory, i'd would even go so far as to say, just replace its internal malloc call (same goes for _aligned_free), this allows allocation with only a single system call.

However, why do you need such massive alignment? Are you trying to abuse address bit patterns for fast memory block slabs?

Necrolis
  • 25,836
  • 3
  • 63
  • 101