Cheng Shao pushed to branch wip/hugepages at Glasgow Haskell Compiler / GHC
Commits:
-
4cfbe8ff
by Teo Camarasu at 2026-03-11T04:46:04+00:00
9 changed files:
- docs/users_guide/runtime_control.rst
- rts/RtsFlags.c
- rts/configure.ac
- rts/include/rts/Flags.h
- rts/posix/OSMem.c
- rts/sm/BlockAlloc.c
- rts/sm/OSMem.h
- testsuite/tests/rts/all.T
- + testsuite/tests/rts/testhugepagesmblockalloc.c
Changes:
| ... | ... | @@ -384,6 +384,16 @@ Miscellaneous RTS options |
| 384 | 384 | If given, instruct the runtime linker to try to continue linking in the
|
| 385 | 385 | presence of an unresolved symbol.
|
| 386 | 386 | |
| 387 | +.. rts-flag:: -xH
|
|
| 388 | + |
|
| 389 | + This option enables using huge pages to back memory allocations.
|
|
| 390 | + Use of huge pages can make memory lookups more efficient for applications
|
|
| 391 | + with high memory usage.
|
|
| 392 | + Currently we only support 2MB hugepages on Linux.
|
|
| 393 | + |
|
| 394 | + If huge pages aren't available to back allocations, then we fall back to
|
|
| 395 | + regular pages.
|
|
| 396 | + |
|
| 387 | 397 | .. _rts-options-gc:
|
| 388 | 398 | |
| 389 | 399 | RTS options to control the garbage collector
|
| ... | ... | @@ -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 | |
| ... | ... | @@ -570,7 +571,10 @@ usage_text[] = { |
| 570 | 571 | #endif
|
| 571 | 572 | " -xq The allocation limit given to a thread after it receives",
|
| 572 | 573 | " an AllocationLimitExceeded exception. (default: 100k)",
|
| 574 | +#if defined(HUGEPAGE_FLAGS)
|
|
| 575 | +" -xH Try to use hugepages to allocate memory.",
|
|
| 573 | 576 | "",
|
| 577 | +#endif
|
|
| 574 | 578 | #if defined(USE_LARGE_ADDRESS_SPACE)
|
| 575 | 579 | " -xr The size of virtual memory address space reserved by the",
|
| 576 | 580 | " two step allocator (default: 1T)",
|
| ... | ... | @@ -1848,11 +1852,11 @@ error = true; |
| 1848 | 1852 | */
|
| 1849 | 1853 | |
| 1850 | 1854 | case 'q':
|
| 1851 | - OPTION_UNSAFE;
|
|
| 1852 | - RtsFlags.GcFlags.allocLimitGrace
|
|
| 1853 | - = decodeSize(rts_argv[arg], 3, BLOCK_SIZE, HS_INT_MAX)
|
|
| 1854 | - / BLOCK_SIZE;
|
|
| 1855 | - break;
|
|
| 1855 | + OPTION_UNSAFE;
|
|
| 1856 | + RtsFlags.GcFlags.allocLimitGrace
|
|
| 1857 | + = decodeSize(rts_argv[arg], 3, BLOCK_SIZE, HS_INT_MAX)
|
|
| 1858 | + / BLOCK_SIZE;
|
|
| 1859 | + break;
|
|
| 1856 | 1860 | |
| 1857 | 1861 | case 'r':
|
| 1858 | 1862 | OPTION_UNSAFE;
|
| ... | ... | @@ -1860,7 +1864,16 @@ error = true; |
| 1860 | 1864 | = decodeSize(rts_argv[arg], 3, MBLOCK_SIZE, HS_WORD64_MAX);
|
| 1861 | 1865 | break;
|
| 1862 | 1866 | |
| 1863 | - default:
|
|
| 1867 | + case 'H':
|
|
| 1868 | + OPTION_UNSAFE;
|
|
| 1869 | +#if defined(HUGEPAGE_FLAGS)
|
|
| 1870 | + RtsFlags.GcFlags.hugepages = true;
|
|
| 1871 | +#else
|
|
| 1872 | + errorBelch("Program not compiled with hugepages support.");
|
|
| 1873 | +#endif
|
|
| 1874 | + break;
|
|
| 1875 | + |
|
| 1876 | + default:
|
|
| 1864 | 1877 | OPTION_SAFE;
|
| 1865 | 1878 | errorBelch("unknown RTS option: %s",rts_argv[arg]);
|
| 1866 | 1879 | error = true;
|
| ... | ... | @@ -96,7 +96,7 @@ dnl off_t, because it will affect the result of that test. |
| 96 | 96 | AC_SYS_LARGEFILE
|
| 97 | 97 | |
| 98 | 98 | 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])
|
|
| 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 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 | 100 | |
| 101 | 101 | dnl sys/cpuset.h needs sys/param.h to be included first on FreeBSD 9.1; #7708
|
| 102 | 102 | AC_CHECK_HEADERS([sys/cpuset.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] */
|
| ... | ... | @@ -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
|
| ... | ... | @@ -457,6 +478,7 @@ StgWord64 getPhysicalMemorySize (void) |
| 457 | 478 | |
| 458 | 479 | #if defined(USE_LARGE_ADDRESS_SPACE)
|
| 459 | 480 | |
| 481 | + |
|
| 460 | 482 | static void *
|
| 461 | 483 | osTryReserveHeapMemory (W_ len, void *hint)
|
| 462 | 484 | {
|
| ... | ... | @@ -470,6 +492,7 @@ osTryReserveHeapMemory (W_ len, void *hint) |
| 470 | 492 | and then we discard what we don't need */
|
| 471 | 493 | |
| 472 | 494 | base = my_mmap(hint, len + MBLOCK_SIZE, MEM_RESERVE);
|
| 495 | + |
|
| 473 | 496 | if (base == NULL)
|
| 474 | 497 | return NULL;
|
| 475 | 498 |
| ... | ... | @@ -25,7 +25,8 @@ |
| 25 | 25 | |
| 26 | 26 | #include <string.h>
|
| 27 | 27 | |
| 28 | -static void initMBlock(void *mblock, uint32_t node);
|
|
| 28 | +static void initMBlock(void *mblock, uint32_t node);
|
|
| 29 | +static void free_mega_group (bdescr *mg);
|
|
| 29 | 30 | |
| 30 | 31 | /*
|
| 31 | 32 | * By default the DEBUG RTS is built with block allocator assertions
|
| ... | ... | @@ -505,13 +506,30 @@ alloc_mega_group (uint32_t node, StgWord mblocks) |
| 505 | 506 | else
|
| 506 | 507 | {
|
| 507 | 508 | void *mblock;
|
| 509 | + StgWord hugepage_mblocks;
|
|
| 510 | + if(RtsFlags.GcFlags.hugepages) {
|
|
| 511 | + // Round up allocation to hugepage size
|
|
| 512 | + hugepage_mblocks = MBLOCK_ROUND_UP_HUGEPAGE(mblocks);
|
|
| 513 | + }
|
|
| 514 | + else {
|
|
| 515 | + hugepage_mblocks = mblocks;
|
|
| 516 | + }
|
|
| 517 | + |
|
| 508 | 518 | if (RtsFlags.GcFlags.numa) {
|
| 509 | - mblock = getMBlocksOnNode(node, mblocks);
|
|
| 519 | + mblock = getMBlocksOnNode(node, hugepage_mblocks);
|
|
| 510 | 520 | } else {
|
| 511 | - mblock = getMBlocks(mblocks);
|
|
| 521 | + mblock = getMBlocks(hugepage_mblocks);
|
|
| 512 | 522 | }
|
| 513 | 523 | initMBlock(mblock, node); // only need to init the 1st one
|
| 514 | 524 | bd = FIRST_BDESCR(mblock);
|
| 525 | + |
|
| 526 | + // Free the slop
|
|
| 527 | + if(hugepage_mblocks > mblocks) {
|
|
| 528 | + bdescr *mblock_slop_bd = FIRST_BDESCR((uintptr_t)mblock + (uintptr_t)mblocks*MBLOCK_SIZE);
|
|
| 529 | + initMBlock(MBLOCK_ROUND_DOWN(mblock_slop_bd), node);
|
|
| 530 | + mblock_slop_bd->blocks = MBLOCK_GROUP_BLOCKS(hugepage_mblocks - mblocks);
|
|
| 531 | + free_mega_group(mblock_slop_bd);
|
|
| 532 | + }
|
|
| 515 | 533 | }
|
| 516 | 534 | bd->blocks = MBLOCK_GROUP_BLOCKS(mblocks);
|
| 517 | 535 | return bd;
|
| ... | ... | @@ -839,7 +857,7 @@ coalesce_mblocks (bdescr *p) |
| 839 | 857 | return q;
|
| 840 | 858 | }
|
| 841 | 859 | |
| 842 | -static void
|
|
| 860 | +void
|
|
| 843 | 861 | free_mega_group (bdescr *mg)
|
| 844 | 862 | {
|
| 845 | 863 | bdescr *bd, *prev;
|
| ... | ... | @@ -1226,10 +1244,17 @@ uint32_t returnMemoryToOS(uint32_t n /* megablocks */) |
| 1226 | 1244 | return 0;
|
| 1227 | 1245 | #else
|
| 1228 | 1246 | bdescr *bd;
|
| 1247 | + bdescr *rejects;
|
|
| 1248 | + bdescr *next;
|
|
| 1229 | 1249 | uint32_t node;
|
| 1230 | - StgWord size;
|
|
| 1250 | + StgWord size, unaligned_size, freeable_size;
|
|
| 1231 | 1251 | uint32_t init_n;
|
| 1232 | 1252 | init_n = n;
|
| 1253 | + if(RtsFlags.GcFlags.hugepages) {
|
|
| 1254 | + // Invariant: n is always a multiple of the hugepage size
|
|
| 1255 | + // as we can only free whole hugepages.
|
|
| 1256 | + n = MBLOCK_ROUND_DOWN_HUGEPAGE(n);
|
|
| 1257 | + }
|
|
| 1233 | 1258 | |
| 1234 | 1259 | // TODO: This is inefficient because this loop will essentially result in
|
| 1235 | 1260 | // quadratic runtime behavior: for each call to `freeMBlocks`, the
|
| ... | ... | @@ -1242,22 +1267,72 @@ uint32_t returnMemoryToOS(uint32_t n /* megablocks */) |
| 1242 | 1267 | // ToDo: not fair, we free all the memory starting with node 0.
|
| 1243 | 1268 | for (node = 0; n > 0 && node < n_numa_nodes; node++) {
|
| 1244 | 1269 | bd = free_mblock_list[node];
|
| 1270 | + // 'rejects' is a reversed list of mblocks that need to go back on the
|
|
| 1271 | + // free list.
|
|
| 1272 | + rejects = NULL;
|
|
| 1245 | 1273 | while ((n > 0) && (bd != NULL)) {
|
| 1246 | 1274 | size = BLOCKS_TO_MBLOCKS(bd->blocks);
|
| 1247 | - if (size > n) {
|
|
| 1248 | - StgWord newSize = size - n;
|
|
| 1249 | - char *freeAddr = MBLOCK_ROUND_DOWN(bd->start);
|
|
| 1250 | - freeAddr += newSize * MBLOCK_SIZE;
|
|
| 1251 | - bd->blocks = MBLOCK_GROUP_BLOCKS(newSize);
|
|
| 1252 | - freeMBlocks(freeAddr, n);
|
|
| 1253 | - n = 0;
|
|
| 1275 | + next = bd->link;
|
|
| 1276 | + char *aligned_start;
|
|
| 1277 | + |
|
| 1278 | + if(RtsFlags.GcFlags.hugepages) {
|
|
| 1279 | + // we can only free hugepage aligned mblock groups
|
|
| 1280 | + aligned_start = (char*)MBLOCK_ROUND_DOWN(bd) + ((uintptr_t)MBLOCK_ROUND_DOWN(bd) & HUGEPAGE_MASK);
|
|
| 1281 | + unaligned_size = (aligned_start - (char*)MBLOCK_ROUND_DOWN(bd)) / MBLOCK_SIZE;
|
|
| 1282 | + freeable_size = MBLOCK_ROUND_DOWN_HUGEPAGE(size - unaligned_size);
|
|
| 1254 | 1283 | }
|
| 1255 | 1284 | else {
|
| 1256 | - char *freeAddr = MBLOCK_ROUND_DOWN(bd->start);
|
|
| 1257 | - n -= size;
|
|
| 1258 | - bd = bd->link;
|
|
| 1259 | - freeMBlocks(freeAddr, size);
|
|
| 1285 | + aligned_start = (char*)MBLOCK_ROUND_DOWN(bd);
|
|
| 1286 | + unaligned_size = 0;
|
|
| 1287 | + freeable_size = size;
|
|
| 1260 | 1288 | }
|
| 1289 | + |
|
| 1290 | + // We cannot free more than n
|
|
| 1291 | + // Note: n is a multiple of the hugepage size,
|
|
| 1292 | + // so freeable_size will also continue to be a multiple.
|
|
| 1293 | + freeable_size = stg_min(n, freeable_size);
|
|
| 1294 | + |
|
| 1295 | + // Place the front unaligned section back on the list.
|
|
| 1296 | + // If we can't free any of it then this is the entire thing.
|
|
| 1297 | + if (unaligned_size > 0 || freeable_size == 0) {
|
|
| 1298 | + bd->link = rejects;
|
|
| 1299 | + rejects = bd;
|
|
| 1300 | + // We are freeing some mblocks from the middle
|
|
| 1301 | + if (freeable_size > 0) {
|
|
| 1302 | + bd->blocks = MBLOCK_GROUP_BLOCKS(unaligned_size);
|
|
| 1303 | + bdescr *aligned_bd;
|
|
| 1304 | + aligned_bd = FIRST_BDESCR(aligned_start);
|
|
| 1305 | + aligned_bd->blocks = MBLOCK_GROUP_BLOCKS(freeable_size);
|
|
| 1306 | + }
|
|
| 1307 | + }
|
|
| 1308 | + |
|
| 1309 | + if(freeable_size > 0) {
|
|
| 1310 | + // Free the mblocks
|
|
| 1311 | + n -= freeable_size;
|
|
| 1312 | + freeMBlocks(aligned_start, freeable_size);
|
|
| 1313 | + // add the slop to the rejects list
|
|
| 1314 | + if (size - unaligned_size - freeable_size > 0)
|
|
| 1315 | + {
|
|
| 1316 | + void *slop = aligned_start + freeable_size * MBLOCK_SIZE;
|
|
| 1317 | + bdescr* slop_bd = FIRST_BDESCR(slop);
|
|
| 1318 | + slop_bd->blocks = MBLOCK_GROUP_BLOCKS(size - unaligned_size - freeable_size);
|
|
| 1319 | + slop_bd->link = rejects;
|
|
| 1320 | + initMBlock(slop, node);
|
|
| 1321 | + rejects = slop_bd;
|
|
| 1322 | + }
|
|
| 1323 | + }
|
|
| 1324 | + bd = next;
|
|
| 1325 | + }
|
|
| 1326 | + // Place the rejected mblocks back on the free list.
|
|
| 1327 | + // Note: this preserves the order.
|
|
| 1328 | + while(rejects) {
|
|
| 1329 | + // pop the top of the rejects list.
|
|
| 1330 | + next = rejects;
|
|
| 1331 | + rejects = next->link;
|
|
| 1332 | + // place it back on the free list.
|
|
| 1333 | + next->link = bd;
|
|
| 1334 | + ASSERT(next < bd || bd == NULL);
|
|
| 1335 | + bd = next;
|
|
| 1261 | 1336 | }
|
| 1262 | 1337 | free_mblock_list[node] = bd;
|
| 1263 | 1338 | }
|
| ... | ... | @@ -8,7 +8,21 @@ |
| 8 | 8 | |
| 9 | 9 | #pragma once
|
| 10 | 10 | |
| 11 | +#if defined(HAVE_LINUX_MMAN_H)
|
|
| 12 | +#include <linux/mman.h>
|
|
| 13 | + |
|
| 14 | +#define HUGEPAGE_SHIFT 21
|
|
| 15 | +#define HUGEPAGE_FLAGS (MAP_HUGETLB | MAP_HUGE_2MB)
|
|
| 16 | +#else
|
|
| 17 | +#define HUGEPAGE_SHIFT MBLOCK_SHIFT
|
|
| 18 | +#endif
|
|
| 19 | + |
|
| 11 | 20 | #include "BeginPrivate.h"
|
| 21 | +GHC_STATIC_ASSERT(HUGEPAGE_SHIFT >= MBLOCK_SHIFT, "mblock size must not exceed 2MB huge page size");
|
|
| 22 | +#define HUGEPAGE_SIZE (1 << HUGEPAGE_SHIFT)
|
|
| 23 | +#define HUGEPAGE_MASK ((1 << HUGEPAGE_SHIFT) - 1)
|
|
| 24 | +#define MBLOCK_ROUND_DOWN_HUGEPAGE(x) ((x) & ~(HUGEPAGE_SHIFT - MBLOCK_SHIFT))
|
|
| 25 | +#define MBLOCK_ROUND_UP_HUGEPAGE(x) ((x) + ((x) & (HUGEPAGE_SHIFT - MBLOCK_SHIFT)))
|
|
| 12 | 26 | |
| 13 | 27 | void osMemInit(void);
|
| 14 | 28 | void *osGetMBlocks(uint32_t n);
|
| ... | ... | @@ -15,6 +15,12 @@ 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 | + compile_and_run, [''])
|
|
| 23 | + |
|
| 18 | 24 | |
| 19 | 25 | # See bug #101, test requires +RTS -c (or equivalently +RTS -M<something>)
|
| 20 | 26 | # 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 | +} |