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
-
76c19dd0
by Teo Camarasu at 2026-07-20T18:10:41+01:00
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:
| ... | ... | @@ -378,6 +378,16 @@ Miscellaneous RTS options |
| 378 | 378 | If given, instruct the runtime linker to try to continue linking in the
|
| 379 | 379 | presence of an unresolved symbol.
|
| 380 | 380 | |
| 381 | +.. rts-flag:: -xH
|
|
| 382 | + |
|
| 383 | + This option enables using huge pages to back memory allocations.
|
|
| 384 | + Use of huge pages can make memory lookups more efficient for applications
|
|
| 385 | + with high memory usage.
|
|
| 386 | + Currently we only support 2MB hugepages on Linux.
|
|
| 387 | + |
|
| 388 | + If huge pages aren't available to back allocations, then we fall back to
|
|
| 389 | + regular pages.
|
|
| 390 | + |
|
| 381 | 391 | .. _rts-options-gc:
|
| 382 | 392 | |
| 383 | 393 | RTS options to control the garbage collector
|
| ... | ... | @@ -321,6 +321,10 @@ The supported transformers are listed below: |
| 321 | 321 | <td><code>hie_files</code></td>
|
| 322 | 322 | <td>Produce hie files for stage1 libraries</td>
|
| 323 | 323 | </tr>
|
| 324 | + <tr>
|
|
| 325 | + <td><code>hugepages</code></td>
|
|
| 326 | + <td>Enable support for hugepages in the RTS.</td>
|
|
| 327 | + </tr>
|
|
| 324 | 328 | </table>
|
| 325 | 329 | |
| 326 | 330 | ## Ways
|
| ... | ... | @@ -25,6 +25,7 @@ module Flavour |
| 25 | 25 | , enableTextWithSIMDUTF
|
| 26 | 26 | , enableHieFiles
|
| 27 | 27 | , omitPragmas
|
| 28 | + , enableHugepages
|
|
| 28 | 29 | |
| 29 | 30 | , completeSetting
|
| 30 | 31 | , applySettings
|
| ... | ... | @@ -86,6 +87,7 @@ flavourTransformers = M.fromList |
| 86 | 87 | , "dump_stg" =: enableDumpStg
|
| 87 | 88 | , "hash_unit_ids" =: enableHashUnitIds
|
| 88 | 89 | , "hie_files" =: enableHieFiles
|
| 90 | + , "hugepages" =: enableHugepages
|
|
| 89 | 91 | ]
|
| 90 | 92 | where (=:) = (,)
|
| 91 | 93 | |
| ... | ... | @@ -316,6 +318,14 @@ enableUBSan = |
| 316 | 318 | builder Testsuite ? arg "--config=have_ubsan=True"
|
| 317 | 319 | ]
|
| 318 | 320 | |
| 321 | +enableHugepages :: Flavour -> Flavour
|
|
| 322 | +enableHugepages =
|
|
| 323 | + addArgs $
|
|
| 324 | + mconcat [package rts
|
|
| 325 | + ? builder (Cabal Setup)
|
|
| 326 | + ? arg "--configure-option=--enable-hugepages"
|
|
| 327 | + ]
|
|
| 328 | + |
|
| 319 | 329 | -- | Use the LLVM backend in target stages
|
| 320 | 330 | viaLlvmBackend :: Flavour -> Flavour
|
| 321 | 331 | viaLlvmBackend = addArgs $ staged buildingForTarget ? builder Ghc ? arg "-fllvm"
|
| ... | ... | @@ -20,6 +20,7 @@ import qualified Hadrian.Builder.Tar |
| 20 | 20 | |
| 21 | 21 | import CommandLine
|
| 22 | 22 | import Expression
|
| 23 | +import Flavour
|
|
| 23 | 24 | import Flavour.Type
|
| 24 | 25 | import Oracles.Flag
|
| 25 | 26 | import Oracles.Setting
|
| ... | ... | @@ -286,7 +287,7 @@ defaultSourceArgs = SourceArgs |
| 286 | 287 | -- | Default build flavour. Other build flavours are defined in modules
|
| 287 | 288 | -- @Settings.Flavours.*@. Users can add new build flavours in "UserSettings".
|
| 288 | 289 | defaultFlavour :: Flavour
|
| 289 | -defaultFlavour = Flavour
|
|
| 290 | +defaultFlavour = enableHugepages $ Flavour
|
|
| 290 | 291 | { name = "default"
|
| 291 | 292 | , extraArgs = defaultExtraArgs
|
| 292 | 293 | , packages = defaultPackages
|
| ... | ... | @@ -182,6 +182,7 @@ void initRtsFlagsDefaults(void) |
| 182 | 182 | RtsFlags.GcFlags.allocLimitGrace = (100*1024) / BLOCK_SIZE;
|
| 183 | 183 | RtsFlags.GcFlags.numa = false;
|
| 184 | 184 | RtsFlags.GcFlags.numaMask = 1;
|
| 185 | + RtsFlags.GcFlags.hugepages = false;
|
|
| 185 | 186 | RtsFlags.GcFlags.ringBell = false;
|
| 186 | 187 | RtsFlags.GcFlags.longGCSync = 0; /* detection turned off */
|
| 187 | 188 | |
| ... | ... | @@ -572,7 +573,10 @@ usage_text[] = { |
| 572 | 573 | #endif
|
| 573 | 574 | " -xq The allocation limit given to a thread after it receives",
|
| 574 | 575 | " an AllocationLimitExceeded exception. (default: 100k)",
|
| 576 | +#if defined(HUGEPAGE_FLAGS)
|
|
| 577 | +" -xH Try to use hugepages to allocate memory.",
|
|
| 575 | 578 | "",
|
| 579 | +#endif
|
|
| 576 | 580 | #if defined(USE_LARGE_ADDRESS_SPACE)
|
| 577 | 581 | " -xr The size of virtual memory address space reserved by the",
|
| 578 | 582 | " two step allocator (default: 1T)",
|
| ... | ... | @@ -1850,11 +1854,11 @@ error = true; |
| 1850 | 1854 | */
|
| 1851 | 1855 | |
| 1852 | 1856 | case 'q':
|
| 1853 | - OPTION_UNSAFE;
|
|
| 1854 | - RtsFlags.GcFlags.allocLimitGrace
|
|
| 1855 | - = decodeSize(rts_argv[arg], 3, BLOCK_SIZE, HS_INT_MAX)
|
|
| 1856 | - / BLOCK_SIZE;
|
|
| 1857 | - break;
|
|
| 1857 | + OPTION_UNSAFE;
|
|
| 1858 | + RtsFlags.GcFlags.allocLimitGrace
|
|
| 1859 | + = decodeSize(rts_argv[arg], 3, BLOCK_SIZE, HS_INT_MAX)
|
|
| 1860 | + / BLOCK_SIZE;
|
|
| 1861 | + break;
|
|
| 1858 | 1862 | |
| 1859 | 1863 | case 'r':
|
| 1860 | 1864 | OPTION_UNSAFE;
|
| ... | ... | @@ -1862,7 +1866,16 @@ error = true; |
| 1862 | 1866 | = decodeSize(rts_argv[arg], 3, MBLOCK_SIZE, HS_WORD64_MAX);
|
| 1863 | 1867 | break;
|
| 1864 | 1868 | |
| 1865 | - default:
|
|
| 1869 | + case 'H':
|
|
| 1870 | + OPTION_UNSAFE;
|
|
| 1871 | +#if defined(HUGEPAGE_FLAGS)
|
|
| 1872 | + RtsFlags.GcFlags.hugepages = true;
|
|
| 1873 | +#else
|
|
| 1874 | + errorBelch("Program not compiled with hugepages support.");
|
|
| 1875 | +#endif
|
|
| 1876 | + break;
|
|
| 1877 | + |
|
| 1878 | + default:
|
|
| 1866 | 1879 | OPTION_SAFE;
|
| 1867 | 1880 | errorBelch("unknown RTS option: %s",rts_argv[arg]);
|
| 1868 | 1881 | error = true;
|
| ... | ... | @@ -37,6 +37,17 @@ if test "$enable_asserts_all_ways" = "yes" ; then |
| 37 | 37 | AC_DEFINE([USE_ASSERTS_ALL_WAYS], [1], [Compile-in ASSERTs in all ways.])
|
| 38 | 38 | fi
|
| 39 | 39 | |
| 40 | +AC_ARG_ENABLE(hugepages,
|
|
| 41 | +[AS_HELP_STRING([--enable-hugepages],
|
|
| 42 | + [Enable hugepages and set the MBlock size to 2MB.])],
|
|
| 43 | + [FP_CAPITALIZE_YES_NO(["$enableval"], [EnableHugepages])],
|
|
| 44 | + [EnableHugepages=NO]
|
|
| 45 | +)
|
|
| 46 | +if test "$enable_hugepages" = "yes" ; then
|
|
| 47 | + AC_DEFINE([USE_HUGEPAGES], [1], [Enable support for hugepages and set MBlock size to 2MB.])
|
|
| 48 | +fi
|
|
| 49 | + |
|
| 50 | + |
|
| 40 | 51 | # We have to run these unconditionally, but we may discard their
|
| 41 | 52 | # results in the following code
|
| 42 | 53 | AC_CANONICAL_BUILD
|
| ... | ... | @@ -96,7 +107,7 @@ dnl off_t, because it will affect the result of that test. |
| 96 | 107 | AC_SYS_LARGEFILE
|
| 97 | 108 | |
| 98 | 109 | dnl ** check for specific header (.h) files that we are interested in
|
| 99 | -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])
|
|
| 110 | +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])
|
|
| 100 | 111 | |
| 101 | 112 | dnl sys/cpuset.h needs sys/param.h to be included first on FreeBSD 9.1; #7708
|
| 102 | 113 | AC_CHECK_HEADERS([sys/cpuset.h], [], [],
|
| ... | ... | @@ -171,6 +171,8 @@ |
| 171 | 171 | /* The size of a megablock (2^MBLOCK_SHIFT bytes) */
|
| 172 | 172 | #if defined(wasm32_HOST_ARCH)
|
| 173 | 173 | #define MBLOCK_SHIFT 16
|
| 174 | +#elif defined(USE_HUGEPAGES)
|
|
| 175 | +#define MBLOCK_SHIFT 21
|
|
| 174 | 176 | #else
|
| 175 | 177 | #define MBLOCK_SHIFT 20
|
| 176 | 178 | #endif
|
| ... | ... | @@ -91,6 +91,7 @@ typedef struct _GC_FLAGS { |
| 91 | 91 | StgWord numaMask;
|
| 92 | 92 | |
| 93 | 93 | StgWord64 addressSpaceSize; /* large address space size in bytes */
|
| 94 | + bool hugepages; /* Enable hugepages support */
|
|
| 94 | 95 | } GC_FLAGS;
|
| 95 | 96 | |
| 96 | 97 | /* See Note [Synchronization of flags and base APIs] */
|
| ... | ... | @@ -73,6 +73,11 @@ |
| 73 | 73 | # endif
|
| 74 | 74 | #endif
|
| 75 | 75 | |
| 76 | +#if defined(HUGEPAGE_FLAGS)
|
|
| 77 | +static int huge_tried = 0;
|
|
| 78 | +static int huge_failed = 0;
|
|
| 79 | +#endif
|
|
| 80 | + |
|
| 76 | 81 | static void *next_request = 0;
|
| 77 | 82 | |
| 78 | 83 | void osMemInit(void)
|
| ... | ... | @@ -233,12 +238,28 @@ my_mmap (void *addr, W_ size, int operation) |
| 233 | 238 | errorBelch("my_mmap(,,MEM_RESERVE) not supported on this platform");
|
| 234 | 239 | # endif
|
| 235 | 240 | } else if (operation == MEM_COMMIT) {
|
| 236 | - flags = MAP_FIXED | MAP_ANON | MAP_PRIVATE;
|
|
| 241 | + flags = MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE;
|
|
| 242 | +#if defined(HUGEPAGE_FLAGS)
|
|
| 243 | + if ( RtsFlags.GcFlags.hugepages &&
|
|
| 244 | + (size & (HUGEPAGE_SIZE - 1)) == 0) {
|
|
| 245 | + huge_tried += 1;
|
|
| 246 | + flags |= HUGEPAGE_FLAGS;
|
|
| 247 | + }
|
|
| 248 | +#endif /* defined(HUGEPAGE_FLAGS) */
|
|
| 237 | 249 | } else {
|
| 238 | 250 | flags = MAP_ANON | MAP_PRIVATE;
|
| 239 | 251 | }
|
| 240 | 252 | |
| 241 | 253 | ret = mmap(addr, size, prot, flags, -1, 0);
|
| 254 | +#if defined(HUGEPAGE_FLAGS)
|
|
| 255 | + // If the mmap failed, and we tried with HUGEPAGE_FLAGS
|
|
| 256 | + // then retry without.
|
|
| 257 | + if (ret == MAP_FAILED && flags & HUGEPAGE_FLAGS){
|
|
| 258 | + huge_failed += 1;
|
|
| 259 | + flags &= ~HUGEPAGE_FLAGS;
|
|
| 260 | + ret = mmap(addr, size, prot, flags, -1, 0);
|
|
| 261 | + }
|
|
| 262 | +#endif
|
|
| 242 | 263 | # if defined(linux_HOST_OS)
|
| 243 | 264 | if (ret == MAP_FAILED && errno == EPERM) {
|
| 244 | 265 | // Linux may return EPERM if it tried to give us
|
| ... | ... | @@ -8,6 +8,12 @@ |
| 8 | 8 | |
| 9 | 9 | #pragma once
|
| 10 | 10 | |
| 11 | +#if defined(HAVE_LINUX_MMAN_H) && defined(USE_HUGEPAGES)
|
|
| 12 | +#include <linux/mman.h>
|
|
| 13 | +#define HUGEPAGE_FLAGS (MAP_HUGETLB | MAP_HUGE_2MB)
|
|
| 14 | +#define HUGEPAGE_SIZE MBLOCK_SIZE
|
|
| 15 | +#endif
|
|
| 16 | + |
|
| 11 | 17 | #include "BeginPrivate.h"
|
| 12 | 18 | |
| 13 | 19 | void osMemInit(void);
|
| ... | ... | @@ -15,6 +15,15 @@ test('testmblockalloc', |
| 15 | 15 | # which will crash because the mblocks we allocate are not in a state
|
| 16 | 16 | # the leak detector is expecting.
|
| 17 | 17 | |
| 18 | +# A variant of the above that tries to use hugepages
|
|
| 19 | +test('testhugepagesmblockalloc',
|
|
| 20 | + [c_src, only_ways(['normal','threaded1']), extra_run_opts('+RTS -I0 -xr0.125T -xH'),
|
|
| 21 | + unless(opsys('linux'), skip), # Huge pages are only currently supported on Linux
|
|
| 22 | + when(arch('wasm32'), skip), # MBlocks can't be freed on wasm32, see Note [Megablock allocator on wasm] in rts
|
|
| 23 | + when(arch('i386'), skip), # Not available under i386
|
|
| 24 | + ],
|
|
| 25 | + compile_and_run, [''])
|
|
| 26 | + |
|
| 18 | 27 | |
| 19 | 28 | # See bug #101, test requires +RTS -c (or equivalently +RTS -M<something>)
|
| 20 | 29 | # only GHCi triggers the bug, but we run the test all ways for completeness.
|
| 1 | +#include "Rts.h"
|
|
| 2 | + |
|
| 3 | +#include <stdio.h>
|
|
| 4 | + |
|
| 5 | +// 16 * 64 == max 1GB
|
|
| 6 | +const int MAXALLOC = 16;
|
|
| 7 | +const int ARRSIZE = 64;
|
|
| 8 | + |
|
| 9 | +const int LOOPS = 1000;
|
|
| 10 | +const int SEED = 0xf00f00;
|
|
| 11 | + |
|
| 12 | +extern StgWord mblocks_allocated;
|
|
| 13 | + |
|
| 14 | +int main (int argc, char *argv[])
|
|
| 15 | +{
|
|
| 16 | + int i, j, b;
|
|
| 17 | + |
|
| 18 | + void *a[ARRSIZE];
|
|
| 19 | + uint32_t sizes[ARRSIZE];
|
|
| 20 | + |
|
| 21 | + srand(SEED);
|
|
| 22 | + |
|
| 23 | + {
|
|
| 24 | + RtsConfig conf = defaultRtsConfig;
|
|
| 25 | + conf.rts_opts_enabled = RtsOptsAll;
|
|
| 26 | + hs_init_ghc(&argc, &argv, conf);
|
|
| 27 | + }
|
|
| 28 | + |
|
| 29 | + // repeatedly sweep though the array, allocating new random-sized
|
|
| 30 | + // objects and deallocating the old ones.
|
|
| 31 | + for (i=0; i < LOOPS; i++)
|
|
| 32 | + {
|
|
| 33 | + for (j=0; j < ARRSIZE; j++)
|
|
| 34 | + {
|
|
| 35 | + if (i > 0)
|
|
| 36 | + {
|
|
| 37 | + freeMBlocks(a[j], sizes[j]);
|
|
| 38 | + }
|
|
| 39 | + b = (rand() % MAXALLOC) + 1;
|
|
| 40 | + a[j] = getMBlocks(b);
|
|
| 41 | + sizes[j] = b;
|
|
| 42 | + }
|
|
| 43 | + }
|
|
| 44 | + |
|
| 45 | + releaseFreeMemory();
|
|
| 46 | + |
|
| 47 | + for (j=0; j < ARRSIZE; j++)
|
|
| 48 | + {
|
|
| 49 | + freeMBlocks(a[j], sizes[j]);
|
|
| 50 | + }
|
|
| 51 | + |
|
| 52 | + releaseFreeMemory();
|
|
| 53 | + |
|
| 54 | + // this time, sweep forwards allocating new blocks, and then
|
|
| 55 | + // backwards deallocating them.
|
|
| 56 | + for (i=0; i < LOOPS; i++)
|
|
| 57 | + {
|
|
| 58 | + for (j=0; j < ARRSIZE; j++)
|
|
| 59 | + {
|
|
| 60 | + b = (rand() % MAXALLOC) + 1;
|
|
| 61 | + a[j] = getMBlocks(b);
|
|
| 62 | + sizes[j] = b;
|
|
| 63 | + }
|
|
| 64 | + for (j=ARRSIZE-1; j >= 0; j--)
|
|
| 65 | + {
|
|
| 66 | + freeMBlocks(a[j], sizes[j]);
|
|
| 67 | + }
|
|
| 68 | + }
|
|
| 69 | + |
|
| 70 | + releaseFreeMemory();
|
|
| 71 | + |
|
| 72 | + hs_exit(); // will do a memory leak test
|
|
| 73 | + |
|
| 74 | + exit(0);
|
|
| 75 | +} |