7

I'm trying to use huge pages for .text segments of my application to reduce TLB misses. I successfully relinked my app with libhugetlbfs support and .text segment of main executable is now being loaded in huge page backed memory.

However main code base of the app is structured as several shared libraries, so I want these libs also be backed by huge pages. Simply linking .so with same options as main app and setting env params do not lead to .so .text segments to be on huge pages.

Is there any way how one could have .so loaded in huge pages? I could do a static linking of my app but it will dramatically complicate a build.

Andrey Ivanov
  • 321
  • 1
  • 4
  • Try to use hugeedit utility for your SO. –  Nov 13 '17 at 13:02
  • 2
    Works for executables, but not for .so unfortunately – Andrey Ivanov Nov 14 '17 at 17:56
  • Hm. Then the only solution I think is to handle it yourself. You have to make it by your hands - allocate hugetables, copy text and data segments into mapping, unmap old mapping of text and data segments of SO and remap huge pages at fixed(MAP_FIXED) address. Sounds like black magic, but I'm sure it is possible. –  Nov 15 '17 at 10:33

1 Answers1

0

You can preload a library that remaps all executable mappings to huge pages like this:

  1. Call the remapping function from the constructor of your preload library, since at that point all other libraries and the executable is already loaded and thus mapped.
  2. Iterate over all r-xp mappings in /proc/self/smaps.
  3. Ignore the mapping of your preload library.
  4. Make sure that you don't call any function from a library that is being re-mapped from you core remapping code. For example, this could even exclude libc functions.
  5. For each mapping, select a sub-range that is huge-page size aligned. NB: The end address can be aligned-up if there is a gap to the next mapping.
  6. Copy that range to a temporary mapping
  7. Re-map that sub-range using hugepages (e.g. via mmap(..., PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_FIXED |MAP_HUGETLB | MAP_HUGE_2MB, ...))
  8. Copy the saved range back
  9. unmap the temporary range
  10. Remove write permission of the re-mapped range via mprotect(..., PROT_READ | PROT_EXEC)

All this can be accomplished in 500 lines of code or so.


Intel's iodlr project includes a preload library that works similar to the above approach.

Example usage:

LD_PRELOAD=/path/to/liblppreload.so IODLR_USE_EXPLICIT_HP=1 /path/to/your/executable

In contrast to the above approach iodlr iterates over ELF headers, thus, in some cases the first and last pages may end up not being remapped.


On Linux, code mappings aren't aligned to - say - 2 MiB hugepage sizes, by default.

Thus, with the above remapping schemes you may end up with a head and tail of each mapping that isn't remapped. Also, a code mapping that is only somewhat larger than 2 MiB may very well not being remapped due to its non-alignment.

However, since the applications where this optimization is feasible usually have much larger code mappings, such a scheme is likely good enough, as is.

However, one can also relink the executable and some libraries with -Wl,-zcommon-page-size=2097152 -Wl,-zmax-page-size=2097152 such that code mappings can be completely remapped. (as of 2023, this isn't recommended for iodlr, though, as it can even lead to effectively less code being remapped)

maxschlepzig
  • 35,645
  • 14
  • 145
  • 182