Teo Camarasu pushed to branch wip/hugepages at Glasgow Haskell Compiler / GHC

Commits:

12 changed files:

Changes:

  • docs/users_guide/runtime_control.rst
    ... ... @@ -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
    

  • hadrian/doc/flavours.md
    ... ... @@ -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
    

  • hadrian/src/Flavour.hs
    ... ... @@ -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"
    

  • hadrian/src/Settings/Default.hs
    ... ... @@ -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
    

  • rts/RtsFlags.c
    ... ... @@ -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;
    

  • rts/configure.ac
    ... ... @@ -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], [], [],
    

  • rts/include/rts/Constants.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
    

  • rts/include/rts/Flags.h
    ... ... @@ -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] */
    

  • rts/posix/OSMem.c
    ... ... @@ -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
    

  • rts/sm/OSMem.h
    ... ... @@ -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);
    

  • testsuite/tests/rts/all.T
    ... ... @@ -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.
    

  • testsuite/tests/rts/testhugepagesmblockalloc.c
    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
    +}