diff --git a/mps/code/arena.c b/mps/code/arena.c index 3d843cc7bde..e5d2901683e 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -243,10 +243,11 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) arena->sig = ArenaSig; AVERT(Arena, arena); - /* Initialise a pool to hold the arena's CBS blocks. This pool can't be - allowed to extend itself using ArenaAlloc because it is used during - ArenaAlloc, so MFSExtendSelf is set to FALSE. Failures to extend are - handled where the Land is used. */ + /* Initialise a pool to hold the CBS blocks for the arena's free + * land. This pool can't be allowed to extend itself using + * ArenaAlloc because it is used to implement ArenaAlloc, so + * MFSExtendSelf is set to FALSE. Failures to extend are handled + * where the free land is used: see arenaFreeLandInsertExtend. */ MPS_ARGS_BEGIN(piArgs) { MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSZonedBlockStruct)); @@ -258,18 +259,6 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) if (res != ResOK) goto failMFSInit; - /* Initialise the freeLand. */ - MPS_ARGS_BEGIN(liArgs) { - MPS_ARGS_ADD(liArgs, CBSBlockPool, ArenaCBSBlockPool(arena)); - res = LandInit(ArenaFreeLand(arena), CBSZonedLandClassGet(), arena, - ArenaGrainSize(arena), arena, liArgs); - } MPS_ARGS_END(liArgs); - AVER(res == ResOK); /* no allocation, no failure expected */ - if (res != ResOK) - goto failLandInit; - /* Note that although freeLand is initialised, it doesn't have any memory - for its blocks, so hasFreeLand remains FALSE until later. */ - /* initialize the reservoir, */ res = ReservoirInit(&arena->reservoirStruct, arena); if (res != ResOK) @@ -279,8 +268,6 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) return ResOK; failReservoirInit: - LandFinish(ArenaFreeLand(arena)); -failLandInit: PoolFinish(ArenaCBSBlockPool(arena)); failMFSInit: GlobalsFinish(ArenaGlobals(arena)); @@ -316,16 +303,33 @@ static Res arenaFreeLandInit(Arena arena) AVER(!arena->hasFreeLand); AVER(arena->primary != NULL); - /* With the primary chunk initialised we can add page memory to the freeLand - * that describes the free address space in the primary chunk. */ + /* Initialise the free land. */ + MPS_ARGS_BEGIN(liArgs) { + MPS_ARGS_ADD(liArgs, CBSBlockPool, ArenaCBSBlockPool(arena)); + res = LandInit(ArenaFreeLand(arena), CBSZonedLandClassGet(), arena, + ArenaGrainSize(arena), arena, liArgs); + } MPS_ARGS_END(liArgs); + AVER(res == ResOK); /* no allocation, no failure expected */ + if (res != ResOK) + goto failLandInit; + + /* With the primary chunk initialised we can add page memory to the + * free land that describes the free address space in the primary + * chunk. */ res = ArenaFreeLandInsert(arena, PageIndexBase(arena->primary, arena->primary->allocBase), arena->primary->limit); if (res != ResOK) - return res; + goto failFreeLandInsert; + arena->hasFreeLand = TRUE; return ResOK; + +failFreeLandInsert: + LandFinish(ArenaFreeLand(arena)); +failLandInit: + return res; } Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) @@ -441,16 +445,16 @@ static void arenaMFSPageFreeVisitor(Pool pool, Addr base, Size size, static void arenaFreeLandFinish(Arena arena) { - /* We must tear down the freeLand before the chunks, because pages - * containing CBS blocks might be allocated in those chunks. */ + AVERT(Arena, arena); AVER(arena->hasFreeLand); - arena->hasFreeLand = FALSE; - LandFinish(ArenaFreeLand(arena)); - + /* The CBS block pool can't free its own memory via ArenaFree because - * that would use the freeLand. */ + * that would use the free land. */ MFSFinishTracts(ArenaCBSBlockPool(arena), arenaMFSPageFreeVisitor, UNUSED_POINTER, UNUSED_SIZE); + + arena->hasFreeLand = FALSE; + LandFinish(ArenaFreeLand(arena)); } void ArenaDestroy(Arena arena) @@ -464,6 +468,8 @@ void ArenaDestroy(Arena arena) ControlFinish(arena); + /* We must tear down the free land before the chunks, because pages + * containing CBS blocks might be allocated in those chunks. */ arenaFreeLandFinish(arena); /* Call class-specific finishing. This will call ArenaFinish. */ @@ -854,7 +860,7 @@ static Res arenaExtendCBSBlockPool(Range pageRangeReturn, Arena arena) return res; MFSExtend(ArenaCBSBlockPool(arena), pageBase, ArenaGrainSize(arena)); - RangeInit(pageRangeReturn, pageBase, AddrAdd(pageBase, ArenaGrainSize(arena))); + RangeInitSize(pageRangeReturn, pageBase, ArenaGrainSize(arena)); return ResOK; } @@ -874,15 +880,19 @@ static void arenaExcludePage(Arena arena, Range pageRange) } -/* arenaLandInsert -- add range to arena's land, maybe extending block pool +/* arenaFreeLandInsertExtend -- add range to arena's free land, maybe + * extending block pool * - * The arena's land can't get memory in the usual way because it is - * used in the basic allocator, so we allocate pages specially. + * The arena's free land can't get memory for its block pool in the + * usual way (via ArenaAlloc), because it is the mechanism behind + * ArenaAlloc! So we extend the block pool via a back door (see + * arenaExtendCBSBlockPool). * * Only fails if it can't get a page for the block pool. */ -static Res arenaLandInsert(Range rangeReturn, Arena arena, Range range) +static Res arenaFreeLandInsertExtend(Range rangeReturn, Arena arena, + Range range) { Res res; @@ -908,16 +918,18 @@ static Res arenaLandInsert(Range rangeReturn, Arena arena, Range range) } -/* ArenaFreeLandInsert -- add range to arena's land, maybe stealing memory +/* arenaFreeLandInsertSteal -- add range to arena's free land, maybe + * stealing memory * - * See arenaLandInsert. This function may only be applied to mapped - * pages and may steal them to store Land nodes if it's unable to - * allocate space for CBS blocks. + * See arenaFreeLandInsertExtend. This function may only be applied to + * mapped pages and may steal them to store Land nodes if it's unable + * to allocate space for CBS blocks. * * IMPORTANT: May update rangeIO. */ -static void arenaLandInsertSteal(Range rangeReturn, Arena arena, Range rangeIO) +static void arenaFreeLandInsertSteal(Range rangeReturn, Arena arena, + Range rangeIO) { Res res; @@ -925,7 +937,7 @@ static void arenaLandInsertSteal(Range rangeReturn, Arena arena, Range rangeIO) AVERT(Arena, arena); AVERT(Range, rangeIO); - res = arenaLandInsert(rangeReturn, arena, rangeIO); + res = arenaFreeLandInsertExtend(rangeReturn, arena, rangeIO); if (res != ResOK) { Addr pageBase; @@ -954,7 +966,8 @@ static void arenaLandInsertSteal(Range rangeReturn, Arena arena, Range rangeIO) } -/* ArenaFreeLandInsert -- add range to arena's land, maybe extending block pool +/* ArenaFreeLandInsert -- add range to arena's free land, maybe extending + * block pool * * The inserted block of address space may not abut any existing block. * This restriction ensures that we don't coalesce chunks and allocate @@ -969,7 +982,7 @@ Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit) AVERT(Arena, arena); RangeInit(&range, base, limit); - res = arenaLandInsert(&oldRange, arena, &range); + res = arenaFreeLandInsertExtend(&oldRange, arena, &range); if (res != ResOK) return res; @@ -984,7 +997,8 @@ Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit) } -/* ArenaFreeLandDelete -- remove range from arena's land, maybe extending block pool +/* ArenaFreeLandDelete -- remove range from arena's free land, maybe + * extending block pool * * This is called from ChunkFinish in order to remove address space from * the arena. @@ -1076,7 +1090,7 @@ static Res arenaAllocFromLand(Tract *tractReturn, ZoneSet zones, Bool high, failMark: { - Res insertRes = arenaLandInsert(&oldRange, arena, &range); + Res insertRes = arenaFreeLandInsertExtend(&oldRange, arena, &range); AVER(insertRes == ResOK); /* We only just deleted it. */ /* If the insert does fail, we lose some address space permanently. */ } @@ -1288,7 +1302,7 @@ void ArenaFree(Addr base, Size size, Pool pool) RangeInit(&range, base, limit); - arenaLandInsertSteal(&oldRange, arena, &range); /* may update range */ + arenaFreeLandInsertSteal(&oldRange, arena, &range); /* may update range */ (*arena->class->free)(RangeBase(&range), RangeSize(&range), pool); diff --git a/mps/code/freelist.c b/mps/code/freelist.c index a9e482550f3..6eddc3dff83 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -1,7 +1,7 @@ /* freelist.c: FREE LIST ALLOCATOR IMPLEMENTATION * * $Id$ - * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2015 Ravenbrook Limited. See end of file for license. * * .sources: . */ @@ -18,11 +18,11 @@ SRCID(freelist, "$Id$"); typedef union FreelistBlockUnion { - struct { + struct FreelistBlockSmall { FreelistBlock next; /* tagged with low bit 1 */ /* limit is (char *)this + freelistAlignment(fl) */ } small; - struct { + struct FreelistBlockLarge { FreelistBlock next; /* not tagged (low bit 0) */ Addr limit; } large; @@ -101,6 +101,9 @@ static Bool FreelistBlockCheck(FreelistBlock block) CHECKL(freelistBlockNext(block) == freelistEND || block < freelistBlockNext(block)); CHECKL(freelistBlockIsSmall(block) || (Addr)block < block->large.limit); + /* Would like to CHECKL(!freelistBlockIsSmall(block) || + * freelistBlockSize(fl, block) == freelistAlignment(fl)) but we + * don't have 'fl' here. This is checked in freelistBlockSetLimit. */ return TRUE; } @@ -139,6 +142,7 @@ static void freelistBlockSetLimit(Freelist fl, FreelistBlock block, Addr limit) } else { AVER(size >= sizeof(block->small)); block->small.next = freelistTagSet(block->small.next); + AVER(freelistBlockSize(fl, block) == freelistAlignment(fl)); } AVER(freelistBlockLimit(fl, block) == limit); } @@ -170,6 +174,9 @@ Bool FreelistCheck(Freelist fl) CHECKS(Freelist, fl); land = FreelistLand(fl); CHECKD(Land, land); + CHECKL(AlignCheck(FreelistMinimumAlignment)); + CHECKL(sizeof(struct FreelistBlockSmall) < sizeof(struct FreelistBlockLarge)); + CHECKL(sizeof(struct FreelistBlockSmall) <= freelistAlignment(fl)); /* See */ CHECKL(AlignIsAligned(freelistAlignment(fl), FreelistMinimumAlignment)); CHECKL((fl->list == freelistEND) == (fl->listSize == 0)); @@ -236,12 +243,14 @@ static Size freelistSize(Land land) * Otherwise, if next is freelistEND, make prev the last block in the list. * Otherwise, make next follow prev in the list. * Update the count of blocks by 'delta'. - + * * It is tempting to try to simplify this code by putting a * FreelistBlockUnion into the FreelistStruct and so avoiding the * special case on prev. But the problem with that idea is that we * can't guarantee that such a sentinel would respect the isolated - * range invariant, and so it would still have to be special-cases. + * range invariant (it would have to be at a lower address than the + * first block in the free list, which the MPS has no mechanism to + * enforce), and so it would still have to be special-cased. */ static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev, @@ -781,6 +790,7 @@ static Res freelistDescribe(Land land, mps_lib_FILE *stream, Count depth) res = WriteF(stream, depth, "Freelist $P {\n", (WriteFP)fl, " listSize = $U\n", (WriteFU)fl->listSize, + " size = $U\n", (WriteFU)fl->size, NULL); b = LandIterate(land, freelistDescribeVisitor, stream, depth + 2); @@ -815,7 +825,7 @@ DEFINE_LAND_CLASS(FreelistLandClass, class) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013-2014 Ravenbrook Limited . + * Copyright (C) 2013-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index 629ffee08fe..34c2eedd4cd 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -76,14 +76,14 @@ class, a subclass of ``LandClass`` suitable for passing to ``LandClass CBSFastLandClassGet(void)`` -_`.function.class`: Returns a subclass of ``CBSLandClass`` that +_`.function.class.fast`: Returns a subclass of ``CBSLandClass`` that maintains, for each subtree, the size of the largest block in that subtree. This enables the ``LandFindFirst()``, ``LandFindLast()``, and ``LandFindLargest()`` generic functions. ``LandClass CBSZonedLandClassGet(void)`` -_`.function.class`: Returns a subclass of ``CBSFastLandClass`` that +_`.function.class.zoned`: Returns a subclass of ``CBSFastLandClass`` that maintains, for each subtree, the union of the zone sets of all ranges in that subtree. This enables the ``LandFindInZones()`` generic function. diff --git a/mps/design/exec-env.txt b/mps/design/exec-env.txt index 5a7a4c5c76d..65de1dd228f 100644 --- a/mps/design/exec-env.txt +++ b/mps/design/exec-env.txt @@ -74,8 +74,8 @@ available, only language facilities. _`.int.free.lib`: We assume that the headers ````, ````, ```` and ```` are available in the freestanding environment, because they define only language features -and not library calls. We assume that we may not make use of any other -definitions if freestanding parts of the system. +and not library calls. We assume that we may not make use of +definitions in any other headers in freestanding parts of the system. _`.int.free.term`: We may not terminate the program in a freestanding environment, and therefore we may not call :c:func:`abort`. We can't @@ -149,7 +149,7 @@ Document History Copyright and License --------------------- -Copyright © 1996-2014 Ravenbrook Limited. All rights reserved. +Copyright © 1996-2015 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/design/land.txt b/mps/design/land.txt index f99ff95baba..df16fc922b9 100644 --- a/mps/design/land.txt +++ b/mps/design/land.txt @@ -86,7 +86,7 @@ indicating whether to continue with the iteration. ``typedef Bool (*LandDeleteVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS)`` -_`.type.visitor`: Type ``LandDeleteVisitor`` is a callback function that may +_`.type.deletevisitor`: Type ``LandDeleteVisitor`` is a callback function that may be passed to ``LandIterateAndDelete()``. It is called for every isolated contiguous range in address order. The function must return a ``Bool`` indicating whether to continue with the iteration. It may additionally @@ -187,6 +187,16 @@ _`.function.iterate.and.delete`: As ``LandIterate()``, but the visitor function additionally returns a Boolean indicating whether the range should be deleted from the land. +_`.function.iterate.and.delete.justify`: The reason for having both +``LandIterate()`` and ``LandIterateAndDelete()`` is that it may be +possible to use a more efficient algorithm, or to preserve more +properties of the data structure, when it is known that the land willl +not be modified during the iteration. For example, in the CBS +implementation, ``LandIterate()`` uses ``TreeTraverse()`` which +preserves the tree structure, whereas ``LandIterateAndDelete()`` uses +``TreeTraverseAndDelete()`` which flattens the tree structure, losing +information about recently accessed nodes. + ``Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)`` _`.function.find.first`: Locate the first block (in address order) @@ -311,7 +321,7 @@ Document History Copyright and License --------------------- -Copyright © 2014 Ravenbrook Limited. All rights reserved. +Copyright © 2014-2015 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/manual/source/pool/amc.rst b/mps/manual/source/pool/amc.rst index 8c74d7119a6..a21594202c6 100644 --- a/mps/manual/source/pool/amc.rst +++ b/mps/manual/source/pool/amc.rst @@ -128,8 +128,10 @@ AMC interface pointers` keep objects alive. * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, - default 4096) is the default :term:`size` of block that the pool - will request from the :term:`arena`. + default 4096) is the minimum :term:`size` of the memory segments + that the pool requests from the :term:`arena`. Larger segments + reduce the per-segment overhead, but increase + :term:`fragmentation` and :term:`retention`. For example:: diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index b3037c28920..b7f580c7286 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -12,6 +12,11 @@ Release 1.115.0 New features ............ +#. When creating an :ref:`pool-amc` pool, :c:func:`mps_pool_create_k` + accepts the new keyword argument :c:macro:`MPS_KEY_EXTEND_BY`, + specifying the minimum size of the memory segments that the pool + requests from the :term:`arena`. + #. The function :c:func:`mps_arena_create_k` accepts two new :term:`keyword arguments`. :c:macro:`MPS_KEY_ARENA_COMMIT_LIMIT` sets the :term:`commit limit` for the arena, and @@ -49,6 +54,30 @@ Other changes .. _job001887: https://www.ravenbrook.com/project/mps/issue/job001887/ +#. :ref:`pool-amc` pools now assert that exact references into the + pool are aligned to the pool's alignment. See job002175_. + + .. _job002175: https://www.ravenbrook.com/project/mps/issue/job002175/ + +#. Internal calculation of the address space available to the MPS no + longer takes time proportional to the number of times the arena has + been extended, speeding up allocation when memory is tight. See + job003814_. + + .. _job003814: https://www.ravenbrook.com/project/mps/issue/job003814/ + +#. Setting :c:macro:`MPS_KEY_SPARE` for a :ref:`pool-mvff` pool now + works. See job003870_. + + .. _job003870: https://www.ravenbrook.com/project/mps/issue/job003870/ + +#. When the arena is out of memory and cannot be extended without + hitting the :term:`commit limit`, the MPS now returns + :c:macro:`MPS_RES_COMMIT_LIMIT` rather than substituting + :c:macro:`MPS_RES_RESOURCE`. See job003899_. + + .. _job003899: https://www.ravenbrook.com/project/mps/issue/job003899/ + .. _release-notes-1.114: @@ -180,8 +209,8 @@ Other changes #. Allocation into :ref:`pool-awl` pools again reliably provokes garbage collections of the generation that the pool belongs to. (In - release 1.113.0, the generation would only be collected if a pool - of some other class allocated into it.) See job003772_. + version 1.113, the generation would only be collected if a pool of + some other class allocated into it.) See job003772_. .. _job003772: https://www.ravenbrook.com/project/mps/issue/job003772/ @@ -193,13 +222,21 @@ Other changes .. _job003773: https://www.ravenbrook.com/project/mps/issue/job003773/ #. The :ref:`pool-mvt` and :ref:`pool-mvff` pool classes are now - around 25% faster (in our benchmarks) than they were in release - 1.113.0. + around 25% faster (in our benchmarks) than they were in version + 1.113. #. The default assertion handler in the default :term:`plinth` now flushes the telemetry stream before aborting. See :c:func:`mps_lib_assert_fail`. +#. Garbage collection performance is substantially improved in the + situation where the arena has been extended many times. Critical + operations now take time logarithmic in the number of times the + arena has been extended (rather than linear, as in version 1.113 + and earlier). See job003554_. + + .. _job003554: https://www.ravenbrook.com/project/mps/issue/job003554/ + .. _release-notes-1.113: diff --git a/mps/test/README b/mps/test/README index 18bc2ed675e..90edaf8857f 100644 --- a/mps/test/README +++ b/mps/test/README @@ -13,7 +13,7 @@ From the test directory:: PLATFORM=lii6ll # substitute your platform CODE=../code # code directory of the branch you are testing - make -C $CODE -f $PLATFORM.gmk VARIETY=cool $PLATFORM/cool/mps.o + make -B -C $CODE -f $PLATFORM.gmk VARIETY=cool $PLATFORM/cool/mps.o alias qa="perl test/qa -i $CODE -l $CODE/$PLATFORM/cool/mps.o" qa clib qa run function/5.c