Currently, AllocateRegion has a tendency to fragment memory: it allocates
2*kRegionSize, and if the memory is aligned, will unmap kRegionSize bytes,
thus creating a hole, which can't itself be reused for another region. This
is exacerbated by the fact that if 2 regions get allocated one after another
without any mmap in between, the second will be aligned due to mappings
generally being contiguous.
An idea, suggested by @alekseyshl, to prevent such a behavior is to have a
stash of regions: if the 2*kRegionSize allocation is properly aligned, split
it in two, and stash the second part to be returned next time a region is
requested.
At this point, I thought about a couple of ways to implement this:
- either an IntrusiveList of regions candidates, storing next at the begining of the region;
- a small array of regions candidates existing in the Primary.
While the second option is more constrained in terms of size, it offers several
advantages:
- security wise, a pointer in a region candidate could be overflowed into, and abused when popping an element;
- we do not dirty the first page of the region by storing something in it;
- unless several threads request regions simultaneously from different size classes, the stash rarely goes above 1 entry.
I am not certain about the Windows impact of this change, as sanitizer_win.cc
has its own version of MmapAlignedOrDie, maybe someone could chime in on this.
MmapAlignedOrDie is effectively unused after this change and could be removed
at a later point. I didn't notice any sizeable performance gain, even though we
are saving a few mmap/munmap syscalls.
I'd just inline map_res + map_size in the only place you use map_end.