[Git][ghc/ghc][wip/hugepages] 2 commits: rts: Implement support for 2MB hugepages
Teo Camarasu pushed to branch wip/hugepages at Glasgow Haskell Compiler / GHC Commits: c8210d78 by Teo Camarasu at 2026-07-20T18:09:44+01:00 rts: Implement support for 2MB hugepages We enable/disable it through a runtime flag (-xH). When enabled we ensure we only (de)allocate in aligned multiples of 2MB. This is only available under x86_64 Linux. It isn't available under wasm and i386. Relates to #24760 Co-authored-by: Matthew Pickering <matthewtpickering@gmail.com> Co-authored-by: Ben Gamari <bgamari.foss@gmail.com> - - - - - 76c19dd0 by Teo Camarasu at 2026-07-20T18:10:41+01:00 DROP ME: enable hugepages in default flavour - - - - - 12 changed files: - docs/users_guide/runtime_control.rst - hadrian/doc/flavours.md - hadrian/src/Flavour.hs - hadrian/src/Settings/Default.hs - rts/RtsFlags.c - rts/configure.ac - rts/include/rts/Constants.h - rts/include/rts/Flags.h - rts/posix/OSMem.c - rts/sm/OSMem.h - testsuite/tests/rts/all.T - + testsuite/tests/rts/testhugepagesmblockalloc.c Changes: ===================================== docs/users_guide/runtime_control.rst ===================================== @@ -378,6 +378,16 @@ Miscellaneous RTS options If given, instruct the runtime linker to try to continue linking in the presence of an unresolved symbol. +.. rts-flag:: -xH + + This option enables using huge pages to back memory allocations. + Use of huge pages can make memory lookups more efficient for applications + with high memory usage. + Currently we only support 2MB hugepages on Linux. + + If huge pages aren't available to back allocations, then we fall back to + regular pages. + .. _rts-options-gc: RTS options to control the garbage collector ===================================== hadrian/doc/flavours.md ===================================== @@ -321,6 +321,10 @@ The supported transformers are listed below: <td><code>hie_files</code></td> <td>Produce hie files for stage1 libraries</td> </tr> + <tr> + <td><code>hugepages</code></td> + <td>Enable support for hugepages in the RTS.</td> + </tr> </table> ## Ways ===================================== hadrian/src/Flavour.hs ===================================== @@ -25,6 +25,7 @@ module Flavour , enableTextWithSIMDUTF , enableHieFiles , omitPragmas + , enableHugepages , completeSetting , applySettings @@ -86,6 +87,7 @@ flavourTransformers = M.fromList , "dump_stg" =: enableDumpStg , "hash_unit_ids" =: enableHashUnitIds , "hie_files" =: enableHieFiles + , "hugepages" =: enableHugepages ] where (=:) = (,) @@ -316,6 +318,14 @@ enableUBSan = builder Testsuite ? arg "--config=have_ubsan=True" ] +enableHugepages :: Flavour -> Flavour +enableHugepages = + addArgs $ + mconcat [package rts + ? builder (Cabal Setup) + ? arg "--configure-option=--enable-hugepages" + ] + -- | Use the LLVM backend in target stages viaLlvmBackend :: Flavour -> Flavour viaLlvmBackend = addArgs $ staged buildingForTarget ? builder Ghc ? arg "-fllvm" ===================================== hadrian/src/Settings/Default.hs ===================================== @@ -20,6 +20,7 @@ import qualified Hadrian.Builder.Tar import CommandLine import Expression +import Flavour import Flavour.Type import Oracles.Flag import Oracles.Setting @@ -286,7 +287,7 @@ defaultSourceArgs = SourceArgs -- | Default build flavour. Other build flavours are defined in modules -- @Settings.Flavours.*@. Users can add new build flavours in "UserSettings". defaultFlavour :: Flavour -defaultFlavour = Flavour +defaultFlavour = enableHugepages $ Flavour { name = "default" , extraArgs = defaultExtraArgs , packages = defaultPackages ===================================== rts/RtsFlags.c ===================================== @@ -182,6 +182,7 @@ void initRtsFlagsDefaults(void) RtsFlags.GcFlags.allocLimitGrace = (100*1024) / BLOCK_SIZE; RtsFlags.GcFlags.numa = false; RtsFlags.GcFlags.numaMask = 1; + RtsFlags.GcFlags.hugepages = false; RtsFlags.GcFlags.ringBell = false; RtsFlags.GcFlags.longGCSync = 0; /* detection turned off */ @@ -572,7 +573,10 @@ usage_text[] = { #endif " -xq The allocation limit given to a thread after it receives", " an AllocationLimitExceeded exception. (default: 100k)", +#if defined(HUGEPAGE_FLAGS) +" -xH Try to use hugepages to allocate memory.", "", +#endif #if defined(USE_LARGE_ADDRESS_SPACE) " -xr The size of virtual memory address space reserved by the", " two step allocator (default: 1T)", @@ -1850,11 +1854,11 @@ error = true; */ case 'q': - OPTION_UNSAFE; - RtsFlags.GcFlags.allocLimitGrace - = decodeSize(rts_argv[arg], 3, BLOCK_SIZE, HS_INT_MAX) - / BLOCK_SIZE; - break; + OPTION_UNSAFE; + RtsFlags.GcFlags.allocLimitGrace + = decodeSize(rts_argv[arg], 3, BLOCK_SIZE, HS_INT_MAX) + / BLOCK_SIZE; + break; case 'r': OPTION_UNSAFE; @@ -1862,7 +1866,16 @@ error = true; = decodeSize(rts_argv[arg], 3, MBLOCK_SIZE, HS_WORD64_MAX); break; - default: + case 'H': + OPTION_UNSAFE; +#if defined(HUGEPAGE_FLAGS) + RtsFlags.GcFlags.hugepages = true; +#else + errorBelch("Program not compiled with hugepages support."); +#endif + break; + + default: OPTION_SAFE; errorBelch("unknown RTS option: %s",rts_argv[arg]); error = true; ===================================== rts/configure.ac ===================================== @@ -37,6 +37,17 @@ if test "$enable_asserts_all_ways" = "yes" ; then AC_DEFINE([USE_ASSERTS_ALL_WAYS], [1], [Compile-in ASSERTs in all ways.]) fi +AC_ARG_ENABLE(hugepages, +[AS_HELP_STRING([--enable-hugepages], + [Enable hugepages and set the MBlock size to 2MB.])], + [FP_CAPITALIZE_YES_NO(["$enableval"], [EnableHugepages])], + [EnableHugepages=NO] +) +if test "$enable_hugepages" = "yes" ; then + AC_DEFINE([USE_HUGEPAGES], [1], [Enable support for hugepages and set MBlock size to 2MB.]) +fi + + # We have to run these unconditionally, but we may discard their # results in the following code AC_CANONICAL_BUILD @@ -96,7 +107,7 @@ dnl off_t, because it will affect the result of that test. AC_SYS_LARGEFILE dnl ** check for specific header (.h) files that we are interested in -AC_CHECK_HEADERS([ctype.h dlfcn.h errno.h fcntl.h limits.h locale.h nlist.h pthread.h signal.h sys/param.h sys/mman.h sys/resource.h sys/select.h sys/time.h sys/timeb.h sys/timerfd.h sys/timers.h sys/times.h sys/utsname.h sys/wait.h termios.h utime.h windows.h winsock.h sched.h]) +AC_CHECK_HEADERS([ctype.h dlfcn.h errno.h fcntl.h limits.h locale.h nlist.h pthread.h signal.h sys/param.h sys/mman.h linux/mman.h sys/resource.h sys/select.h sys/time.h sys/timeb.h sys/timerfd.h sys/timers.h sys/times.h sys/utsname.h sys/wait.h termios.h utime.h windows.h winsock.h sched.h]) dnl sys/cpuset.h needs sys/param.h to be included first on FreeBSD 9.1; #7708 AC_CHECK_HEADERS([sys/cpuset.h], [], [], ===================================== rts/include/rts/Constants.h ===================================== @@ -171,6 +171,8 @@ /* The size of a megablock (2^MBLOCK_SHIFT bytes) */ #if defined(wasm32_HOST_ARCH) #define MBLOCK_SHIFT 16 +#elif defined(USE_HUGEPAGES) +#define MBLOCK_SHIFT 21 #else #define MBLOCK_SHIFT 20 #endif ===================================== rts/include/rts/Flags.h ===================================== @@ -91,6 +91,7 @@ typedef struct _GC_FLAGS { StgWord numaMask; StgWord64 addressSpaceSize; /* large address space size in bytes */ + bool hugepages; /* Enable hugepages support */ } GC_FLAGS; /* See Note [Synchronization of flags and base APIs] */ ===================================== rts/posix/OSMem.c ===================================== @@ -73,6 +73,11 @@ # endif #endif +#if defined(HUGEPAGE_FLAGS) +static int huge_tried = 0; +static int huge_failed = 0; +#endif + static void *next_request = 0; void osMemInit(void) @@ -233,12 +238,28 @@ my_mmap (void *addr, W_ size, int operation) errorBelch("my_mmap(,,MEM_RESERVE) not supported on this platform"); # endif } else if (operation == MEM_COMMIT) { - flags = MAP_FIXED | MAP_ANON | MAP_PRIVATE; + flags = MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE; +#if defined(HUGEPAGE_FLAGS) + if ( RtsFlags.GcFlags.hugepages && + (size & (HUGEPAGE_SIZE - 1)) == 0) { + huge_tried += 1; + flags |= HUGEPAGE_FLAGS; + } +#endif /* defined(HUGEPAGE_FLAGS) */ } else { flags = MAP_ANON | MAP_PRIVATE; } ret = mmap(addr, size, prot, flags, -1, 0); +#if defined(HUGEPAGE_FLAGS) + // If the mmap failed, and we tried with HUGEPAGE_FLAGS + // then retry without. + if (ret == MAP_FAILED && flags & HUGEPAGE_FLAGS){ + huge_failed += 1; + flags &= ~HUGEPAGE_FLAGS; + ret = mmap(addr, size, prot, flags, -1, 0); + } +#endif # if defined(linux_HOST_OS) if (ret == MAP_FAILED && errno == EPERM) { // Linux may return EPERM if it tried to give us ===================================== rts/sm/OSMem.h ===================================== @@ -8,6 +8,12 @@ #pragma once +#if defined(HAVE_LINUX_MMAN_H) && defined(USE_HUGEPAGES) +#include <linux/mman.h> +#define HUGEPAGE_FLAGS (MAP_HUGETLB | MAP_HUGE_2MB) +#define HUGEPAGE_SIZE MBLOCK_SIZE +#endif + #include "BeginPrivate.h" void osMemInit(void); ===================================== testsuite/tests/rts/all.T ===================================== @@ -15,6 +15,15 @@ test('testmblockalloc', # which will crash because the mblocks we allocate are not in a state # the leak detector is expecting. +# A variant of the above that tries to use hugepages +test('testhugepagesmblockalloc', + [c_src, only_ways(['normal','threaded1']), extra_run_opts('+RTS -I0 -xr0.125T -xH'), + unless(opsys('linux'), skip), # Huge pages are only currently supported on Linux + when(arch('wasm32'), skip), # MBlocks can't be freed on wasm32, see Note [Megablock allocator on wasm] in rts + when(arch('i386'), skip), # Not available under i386 + ], + compile_and_run, ['']) + # See bug #101, test requires +RTS -c (or equivalently +RTS -M<something>) # only GHCi triggers the bug, but we run the test all ways for completeness. ===================================== testsuite/tests/rts/testhugepagesmblockalloc.c ===================================== @@ -0,0 +1,75 @@ +#include "Rts.h" + +#include <stdio.h> + +// 16 * 64 == max 1GB +const int MAXALLOC = 16; +const int ARRSIZE = 64; + +const int LOOPS = 1000; +const int SEED = 0xf00f00; + +extern StgWord mblocks_allocated; + +int main (int argc, char *argv[]) +{ + int i, j, b; + + void *a[ARRSIZE]; + uint32_t sizes[ARRSIZE]; + + srand(SEED); + + { + RtsConfig conf = defaultRtsConfig; + conf.rts_opts_enabled = RtsOptsAll; + hs_init_ghc(&argc, &argv, conf); + } + + // repeatedly sweep though the array, allocating new random-sized + // objects and deallocating the old ones. + for (i=0; i < LOOPS; i++) + { + for (j=0; j < ARRSIZE; j++) + { + if (i > 0) + { + freeMBlocks(a[j], sizes[j]); + } + b = (rand() % MAXALLOC) + 1; + a[j] = getMBlocks(b); + sizes[j] = b; + } + } + + releaseFreeMemory(); + + for (j=0; j < ARRSIZE; j++) + { + freeMBlocks(a[j], sizes[j]); + } + + releaseFreeMemory(); + + // this time, sweep forwards allocating new blocks, and then + // backwards deallocating them. + for (i=0; i < LOOPS; i++) + { + for (j=0; j < ARRSIZE; j++) + { + b = (rand() % MAXALLOC) + 1; + a[j] = getMBlocks(b); + sizes[j] = b; + } + for (j=ARRSIZE-1; j >= 0; j--) + { + freeMBlocks(a[j], sizes[j]); + } + } + + releaseFreeMemory(); + + hs_exit(); // will do a memory leak test + + exit(0); +} View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/86a70824fa5e1ebf032e256f7bde0da... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/86a70824fa5e1ebf032e256f7bde0da... You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
participants (1)
-
Teo Camarasu (@teo)