From 6e761dd0bfcd135b92cbecfa1d1fc99e6ec19806 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 12 Mar 2013 18:15:50 +0000 Subject: [PATCH] Design.mps.bt, about 40% converted. Copied from Perforce Change: 181129 ServerID: perforce.ravenbrook.com --- mps/manual/source/design/bt.rst | 689 ++++++++++++++++++++++++++++++++ 1 file changed, 689 insertions(+) create mode 100644 mps/manual/source/design/bt.rst diff --git a/mps/manual/source/design/bt.rst b/mps/manual/source/design/bt.rst new file mode 100644 index 00000000000..573e0a9e5e8 --- /dev/null +++ b/mps/manual/source/design/bt.rst @@ -0,0 +1,689 @@ +.. sources: + + ``_ + +.. mps:prefix:: design.mps.bt + +Bit tables +========== + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer. + +:mps:tag:`intro` This is the design of the Bit Tables module. A Bit +Table is a linear array of bits. A Bit Table of length *n* is indexed +using an integer from 0 up to (but not including) *n*. Each bit in a +Bit Table can hold either the value 0 (aka ``FALSE``) or 1 (aka +``TRUE``). A variety of operations are provided including: set, reset, +and retrieve, individual bits; set and reset a contiguous range of +bits; search for a contiguous range of reset bits; making a "negative +image" copy of a range. + + +History +------- + +:mps:tag:`history.0-3` The history for versions 0-3 is lost pending +possible reconstruction. + +:mps:tag:`history.4` Prepared for review. Added full requirements +section. Made notation more consistent throughout. Documented all +functions. drj 1999-04-29 + + +Definitions +----------- + +:mps:tag:`def.set` **Set** + + Used as a verb meaning to assign the value 1 or ``TRUE`` to a bit. + Used descriptively to denote a bit containing the value 1. Note 1 + and ``TRUE`` are synonyms in MPS C code (see + :mps:ref:`design.mps.type(0).bool.value`). + +:mps:tag:`def.reset` **Reset** + + Used as a verb meaning to assign the value 0 or ``FALSE`` to a + bit. Used descriptively to denote a bit containing the value 0. + Note 0 and ``FALSE`` are synonyms in MPS C code (see + :mps:ref:`design.mps.type(0).bool.value`). + +.. note:: + + Consider using "fill/empty" or "mark/clear" instead of + "set/reset", set/reset is probably a hangover from drj's z80 + hacking days -- drj 1999-04-26 + +:mps:tag:`def.bt` **Bit Table** + + A Bit Table is a mapping from [0,*n*) to {0,1} for some *n*, + represented as a linear array of bits. + + :mps:tag:`.def.bt.justify` They are called *bit tables* because a + single bit is used to encode whether the image of a particular + integer under the map is 0 or 1. + +:mps:tag:`def.range` **Range** + + A contiguous sequence of bits in a Bit Table. Ranges are typically + specified as a *base*--*limit* pair where the range includes the + position specified by the base, but excludes that specified by the + limit. The mathematical interval notation for half-open intervals, + [*base*, *limit*), is used. + + + +Requirements +------------ + +:mps:tag:`req.bit` The storage for a Bit Table of *n* bits shall take +no more than a small constant addition to the storage required for *n* +bits. :mps:tag:`.req.bit.why` This is so that clients can make some +predictions about how much storage their algorithms use. A small +constant is allowed over the minimal for two reasons: inevitable +implementation overheads (such as only being able to allocate storage +in multiples of 32 bits), extra storage for robustness or speed (such +as signature and length fields). + +:mps:tag:`req.create` A means to create Bit Tables. +:mps:tag:`req.create.why` Obvious. + +:mps:tag:`req.destroy` A means to destroy Bit Tables. +:mps:tag:`req.destroy.why` Obvious. + +:mps:tag:`req.ops` The following operations shall be supported: + +* :mps:tag:`req.ops.get` **Get**. Get the value of a bit at a specified + index. +* :mps:tag:`req.ops.set` **Set**. Set a bit at a specified index. +* :mps:tag:`req.ops.reset` **Reset**. Reset a bit at a specified index. + +:mps:tag:`req.ops.minimal.why` Get, Set, Reset, are the minimal +operations. All possible mappings can be created and inspected using +these operations. + +* :mps:tag:`req.ops.set.range` **SetRange**. Set a range of bits. + :mps:tag:`req.ops.set.range.why` It's expected that clients will + often want to set a range of bits; providing this operation allows + the implementation of the BT module to make the operation efficient. +* :mps:tag:`req.ops.reset.range` **ResetRange**. Reset a range of + bits. :mps:tag:`req.ops.reset.range.why` as for SetRange, see + :mps:ref:`.req.ops.set.range.why`. +* :mps:tag:`req.ops.test.range.set` **IsSetRange**. Test whether a range + of bits are all set. :mps:tag:`req.ops.test.range.set.why` Mostly + for checking. For example, often clients will know that a range they + are about to reset is currently all set, they can use this operation + to assert that fact. +* :mps:tag:`req.ops.test.range.reset` **IsResetRange**. Test whether a + range of bits are all reset. :mps:tag:`req.ops.test.range.reset.why` + As for IsSetRange, see :mps:ref:`.req.ops.test.range.set.why`. +* :mps:tag:`req.ops.find` Find a range (which we'll denote [*i*,*j*)) + of at least *L* reset bits that lies in a specified subrange of the + entire Bit Table. Various find operations are required according to + the (additional) properties of the required range: + + * :mps:tag:`req.ops.find.short.low` **FindShortResetRange**. Of all + candidate ranges, find the range with least *j* (find the leftmost + range that has at least *L* reset bits and return just enough of + that). :mps:tag:`req.ops.find.short.low.why` Required by client + and VM arenas to allocate segments. The arenas implement definite + placement policies (such as lowest addressed segment first) so + they need the lowest (or highest) range that will do. It's not + currently useful to allocate segments larger than the requested + size, so finding a short range is sufficient. + * :mps:tag:`req.ops.find.short.high` **FindShortResetRangeHigh**. Of + all candidate ranges, find the range with greatest *i* (find the + rightmost range that has at least *L* reset bits and return just + enough of that). :mps:tag:`req.ops.find.short.high.why` Required + by arenas to implement a specific segment placement policy + (highest addressed segment first). + * :mps:tag:`req.ops.find.long.low` **FindLongResetRange**. Of all + candidate ranges, identify the ranges with least *i* and of those + find the one with greatest *j* (find the leftmost range that has + at least *L* reset bits and return all of it). + :mps:tag:`req.ops.find.long.low.why` Required by the mark and + sweep Pool Classes (AMS, AWL, LO) for allocating objects (filling + a buffer). It's more efficient to fill a buffer with as much + memory as is conveniently possible. There's no strong reason to + find the lowest range but it's bound to have some beneficial + (small) cache effect and makes the algorithm more predictable. + * :mps:tag:`req.ops.find.long.high` **FindLongResetRangeHigh**. + Provided, but not required, see + :mps:ref:`.non-req.ops.find.long.high`. + +* :mps:tag:`req.ops.copy` Copy a range of bits from one Bit Table to another Bit Table. Various copy operations are required: + + * :mps:tag:`req.ops.copy.simple` Copy a range of bits from one Bit + Table to the same position in another Bit Table. + :mps:tag:`req.ops.copy.why` Required to support copying of the + tables for the "low" segment during segment merging and splitting, + for pools using tables (for example, :c:type:`PoolClassAMS`). + * :mps:tag:`req.ops.copy.offset` Copy a range of bits from one Bit + Table to an offset position in another Bit Table. + :mps:tag:`req.ops.copy.why` Required to support copying of the + tables for the "high" segment during segment merging and + splitting, for pools which support this (currently none, as of + 2000-01-17). + * :mps:tag:`req.ops.copy.invert` Copy a range of bits from one Bit + Table to the same position in another Bit Table inverting all the + bits in the target copy. :mps:tag:`req.ops.copy.invert.why` + Required by colour manipulation code in :c:type:`PoolClassAMS` and + :c:type:`PoolClassLO`. + +:mps:tag:`req.speed` Operations shall take no more than a few memory +operations per bit manipulated. :mps:tag:`req.speed.why` Any slower +would be gratuitous. + +:mps:tag:`req.speed.fast` The following operations shall be very fast: + +* :mps:tag:`req.speed.fast.find.short` FindShortResRange (the + operation used to meet :mps:ref:`.req.ops.find.short.low`) + FindShortResRangeHigh (the operation used to meet + :mps:ref:`.req.ops.find.short.high`). + + :mps:tag:`req.speed.fast.find.short.why` These two are used by the + client arena (design.mps.arena.client) and the VM arena + (design.mps.arena.vm) for finding segments in page tables. The + operation will be used sufficiently often that its speed will + noticeably affect the overall speed of the MPS. They will be called + with a length equal to the number of pages in a segment. Typical + values of this length depend on the pool classes used and their + configuration, but we can expect length to be small (1 to 16) + usually. We can expect the Bit Table to be populated densely where + it is populated at all, that is set bits will tend to be clustered + together in subranges. + +* :mps:tag:`req.speed.fast.find.long` FindLongResRange (the operation + used to meet :mps:ref:`.req.ops.find.long.low`) + + :mps:tag:`req.speed.fast.find.long.why` Used in the allocator for + :c:type:`PoolClassAWL` (:mps:ref:`design.mps.poolawl(1)`), + :c:type:`PoolClassAMS` (:mps:ref:`design.mps.poolams(2)`), + :c:type:`PoolClassEPVM` (:mps:ref:`design.mps.poolepvm(0)`). Of + these AWL and EPVM have speed requirements. For AWL the length of + range to be found will be the length of a Dylan table in words. + According to mail.tony.1999-05-05.11-36(0), only + objects are allocated in AWL (though not all objects + are allocated in AWL), and the mean length of an + object is 486 Words. No data for EPVM alas. + +:mps:tag:`req.speed.fast.other.why` We might expect mark and sweep +pools to make use of Bit Tables, the MPS has general requirements to +support efficient mark and sweep pools, so that imposes general speed +requirements on Bit Tables. + + +Non requirements +---------------- + +The following are not requirements but the current design could +support them with little modification or does support them. Often they +used to be requirements, but are no longer, or were added +speculatively or experimentally but aren't currently used. + +* :mps:tag:`non-req.ops.test.range.same` **RangesSame**. Test whether + two ranges that occupy the same positions in different Bit Tables + are the same. This used to be required by :c:type:`PoolClassAMS`, + but is no longer. Currently (1999-05-04) the functionality still + exists. + +* :mps:tag:`non-req.ops.find.long.high` **FindLongResetRangeHigh**. + (see :mps:ref:`.req.ops.find`) Of all candidate ranges, identify the + ranges with greatest *j* and of those find the one with least *i* + (find the rightmost range that has at least *L* reset bits and + return all of it). Provided for symmetry but only currently used by + the BT tests and ``cbstest.c``. + + +Background +---------- + +:mps:tag:`background` Originally Bit Tables were used and implemented +by :c:type:`PoolClassLO` (:mps:ref:`design.mps.poollo`). It was +decided to lift them out into a separate module when designing the +Pool to manage Dylan Weak Tables which is also a mark and sweep pool +and will make use of Bit Tables (see :mps:ref:`design.mps.poolawl`). +:mps:tag:`background.analysis` :mps:ref:`analysis.mps.bt(0)` contains +some of the analysis of the design decisions that were and were not +made in this document. + + +Clients +------- + +:mps:tag:`clients` Bit Tables are used throughout the MPS but the +important uses are in the client and VM arenas +(:mps:ref:`design.mps.arena.client(0)` and +:mps:ref:`design.mps.arena.vm(1)`) a bit table is used to record +whether each page is free or not; several pool classes +(:c:type:`PoolClassLO`, :c:type:`PoolClassEPVM`, +:c:type:`PoolClassAMS`) use bit tables to record which locations are +free and also to store colour. + + +Overview +-------- + +:mps:tag:`over` Mostly, the design is as simple as possible. The +significant complications are iteration (see :mps:ref:`.iteration` +below) and searching (see :mps:ref:`.fun.find-res-range` below) +because both of these are required to be fast. + + +Interface +--------- + +:mps:tag:`if.representation.abstract` A Bit Table is represented by +the type :c:type:`BT`. + +:mps:tag:`if.declare` The module declares a type :c:type:`BT` and a +prototype for each of the functions below. The type is declared in +:mps:ref:`impl.h.mpmtypes`, the prototypes are declared in +:mps:ref:`impl.h.mpm`. Some of the functions are in fact implemented +as macros in the usual way +(:mps:ref:`doc.mps.ref-man.if-conv(0).macro.std`). + +:mps:tag:`if.general.index` Many of the functions specified below take +indexes. If otherwise unspecified an index must be in the interval +[0,*n*) (note, up to, but not including, *n*) where *n* is the number +of bits in the relevant Bit Table (as passed to the :c:func:`BTCreate` +function). + +:mps:tag:`if.general.range` Where a range is specified by two indexes +(*base* and *limit*), the index *base*, which specifies the beginning +of the range, must be in the interval [0,*n*), and the index *limit*, +which specifies the end of the range, must be in the interval [1,*n*] +(note can be *n*), and *base* must be strictly less than *limit* +(empty ranges are not allowed). Sometimes *i* and *j* are used instead +of *base* and *limit*. + +.. c:func:: Res BTCreate(BT *btReturn, Arena arena, Count n) + +:mps:tag:`if.create` Attempts to create a table of length ``n`` in the +arena control pool, putting the table in ``*btReturn``. Returns +:c:macro:`ResOK` if and only if the table is created OK. The initial +values of the bits in the table are undefined (so the client should +probably call :c:func:`BTResRange` on the entire range before using +the :c:type:`BT`). Meets :mps:ref:`.req.create`. + +.. c:func:: void BTDestroy(BT t, Arena arena, Count n) + +:mps:tag:`if.destroy` Destroys the table ``t``, which must have been +created with :c:func:`BTCreate`. The value of argument ``n`` must be +same as the value of the argument passed to :c:func:`BTCreate`. Meets +mps:ref:`.req.destroy`. + +.. c:func:: size_t BTSize(Count n) + +:mps:tag:`if.size` ``BTSize(n)`` returns the number of bytes needed +for a Bit Table of ``n`` bits. :c:func:`BTSize` is a macro, but +``(BTSize)(n)`` will assert if ``n`` exceeds `COUNT_MAX - +MPS_WORD_WIDTH + 1``. This is used by clients that allocate storage +for the :c:type:`BT` themselves. Before :c:func:`BTCreate` and +:c:func:`BTDestroy` were implemented that was the only way to allocate +a Bit Table, but is now deprecated. + +.. c:func:: int BTGet(BT t, Index i) + +:mps:tag:`if.get` ``BTGet(t, i)`` returns the ``i``th bit of the table +``t`` (that is, the image of ``i`` under the mapping). Meets +:mps:ref:`.req.ops.get`. + +.. c:func:: void BTSet(BT t, Index i) + +:mps:tag:`if.set` ``BTSet(t, i)`` sets the ``i``th bit of the table +``t`` (to 1). ``BTGet(t, i)`` will now return 1. Meets +:mps:ref:`.req.ops.set`. + +.if.res: +void BTRes(BT t, Index i); + +BTRes(t, i) resets the ith bit of the table t (to 0). BTGet(t, i) will now +return 0. Meets .req.ops.res. + +.if.set-range: +void BTSetRange(BT t, Index base, Index limit); + +BTSetRange(t, base, limit) sets the range of bits [base, limit) in the table +t. BTGet(t, x) will now return 1 for base<=x> MPS_WORD_SHIFT. The latter +expression is used in the code. :mps:tag:`index.word.justify` The compiler is more +likely to generate good code without the divide. :mps:tag:`index.sub-word` ib is the +"sub-word-index" which is the index of the bit referred to by the bit-index in +the above word. ib = i % MPS_WORD_WIDTH. Since MPS_WORD_WIDTH is a +power-of-two, this is the same as ib = i & ~((Word)-1<>5) + (i&31); } + +:mps:tag:`iteration` Many of the following functions involve iteration over ranges in a +Bit Table. This is performed on whole words rather than individual bits, +whenever possible (to improve speed). This is implemented internally by the +macros ACT_ON_RANGE & ACT_ON_RANGE_HIGH for iterating over the range forwards +and backwards respectively. These macros do not form part of the interface of +the module, but are used extensively in the implementation. The macros are +often used even when speed is not an issue because it simplifies the +implementation and makes it more uniform. The iteration macros take the +parameters (base, limit, single_action, bits_action, word_action). + + base, limit are of type Index and define the range of the iteration + single_action is the name of a macro which will be used for iterating over +bits in the table individually. This macro must take a single Index parameter +corresponding to the index for the bit. The macro must not use break or +continue because it will be called from within a loop from the expansion of +ACT_ON_RANGE. + bits_action is the name of a macro which will be used for iterating over +part-words. This macro must take parameters (wordIndex, base, limit) where +wordIndex is the index into the array of words, and base & limit define a range +of bits within the indexed word. + word_action is the name of a macro which will be used for iterating over +whole-words. This macro must take the parameter (wordIndex) where wordIndex is +the index of the whole-word in the array. The macro must not use break or +continue because it will be called from within a loop from the expansion of +ACT_ON_RANGE. + +:mps:tag:`iteration.exit` The code in the single_action, bits_action, and word_action +macros is allowed to use 'return' or 'goto' to terminate the iteration early. +This is used by the test (.fun.test.*) and find (.fun.find.*) operations. + +:mps:tag:`iteration.small` If the range is sufficiently small only the single_action +macro will be used as this is more efficient in practice. The choice of what +constitutes a small range is made entirely on the basis of experimental +performance results (and currently, 1999-04-27, a "small range" is 6 bits or +fewer. See change.mps.epcore.brisling.160181 for some justification). +Otherwise (for a bigger range) bits_action is used on the part words at either +end of the range (or the whole of the range it if it fits in a single word), +and word_action is used on the words that comprise the inner portion of the +range. + +The implementation of ACT_ON_RANGE (and ACT_ON_RANGE_HIGH) is simple enough. +It decides which macros it should invoke and invokes them. single_action and +word_action are invoked inside loops. + + +:mps:tag:`fun.get` BTGet. +The bit-index will be converted in the usual way, see .index. The relevant +Word will be read out of the Bit Table and shifted right by the sub-Word index +(this brings the relevant bit down to the least significant bit of the Word), +the Word will then be masked with 1 producing the answer. + +:mps:tag:`fun.set` BTSet + +:mps:tag:`fun.res` BTRes + +In both BTSet and BTRes a mask is constructed by shifting 1 left by the +sub-word-index (see .index). For BTSet the mask is ORed into the relevant word +(thereby setting a single bit). For BTRes the mask is inverted and ANDed into +the relevant word (thereby resetting a single bit). + +:mps:tag:`fun.set-range` BTSetRange +ACT_ON_RANGE (see .iteration above) is used with macros that set a single bit +(using BTSet), set a range of bits in a word, and set a whole word. + +:mps:tag:`fun.res-range` BTResRange +This is implemented similarly to BTSetRange (.fun.set-range) except using BTRes +& reverse bit masking logic. + +:mps:tag:`fun.test.range.set` BTIsSetRange +ACT_ON_RANGE (see .iteration above) is used with macros that test whether all +the relevant bits are set; if some of the relevant bits are not set then +'return FALSE' is used to terminate the iteration early and return from the +BTIsSetRange function. If the iteration completes then TRUE is returned. + +:mps:tag:`fun.test.range.reset` BTIsResRange +As for BTIsSetRange (.fun.test.range.set above) but testing whether the bits +are reset. + +:mps:tag:`fun.test.range.same` BTRangesSame +As for BTIsSetRange (.fun.test.range.set above) but testing whether +corresponding ranges in the two Bit Tables are the same. Note there are no +speed requirements, but ACT_ON_RANGE is used for simplicity and uniformity. + +:mps:tag:`fun.find` The four external find functions (BTFindShortResRange, +BTFindShortResRangeHigh, BTFindLongResRange, BTFindLongResRangeHigh) simply +call through to one of the two internal functions: BTFindResRange, +BTFindResRangeHigh. BTFindResRange and BTFindResRangeHigh both have the +following prototype (with a different name obviously): + +Bool BTFindResRange(Index *baseReturn, Index *limitReturn, + BT bt, + Index searchBase, Index searchLimit, + Count minLength, + Count maxLength) + +There are two length parameters, one specifying the minimum length of the range +to be found, the other the maximum length. For BTFindShort* maxLength is equal +to minLength when passed; for BTFindLong* maxLength is equal to the maximum +possible range (searchLimit - searchBase). + +:mps:tag:`fun.find-res-range` BTFindResRange +Iterate within the search boundaries, identifying candidate ranges by searching +for a reset bit. The Boyer-Moore algorithm (reference please?) is used (it's +particularly easy when there are only two symbols, 0 and 1, in the alphabet). +For each candidate range, iterate backwards over the bits from the end of the +range towards the beginning. If a set bit is found, this candidate has failed +and a new candidate range is selected. If when scanning for the set bit a +range of reset bits was found before finding the set bit, then this (small) +range of reset bits is used as the start of the next candidate. Additionally +the end of this small range of reset bits (the end of the failed candidate +range) is remembered so that we don't have to iterate over this range again. +But if no reset bits were found in the candidate range, then iterate again +(starting from the end of the failed candidate) to look for one. If during the +backwards search no set bit is found, then we have found a sufficiently large +range of reset bits; now extend the valid range as far as possible up to the +maximum length by iterating forwards up to the maximum limit looking for a set +bit. The iterations make use of the ACT_ON_RANGE & ACT_ON_RANGE_HIGH macros +using of 'goto' to effect an early termination of the iteration when a +set/reset (as appropriate) bit is found. The macro ACTION_FIND_SET_BIT is used +in the iterations, it efficiently finds the first (that is, with lowest index +or weight) set bit in a word or subword. + +:mps:tag:`fun.find-res-range.improve` Various other performance improvements have been +suggested in the past, including some from request.epcore.170534. Here is a +list of potential improvements which all sound plausible, but which have not +led to performance improvements in practice: + +:mps:tag:`fun.find-res-range.improve.step.partial` When the top index in a candidate +range fails, skip partial words as well as whole words, using +e.g. lookup tables. + +:mps:tag:`fun.find-res-range.improve.lookup` When testing a candidate run, +examine multiple bits at once (e.g. 8), using lookup tables for (e.g) +index of first set bit, index of last set bit, number of reset bits, +length of maximum run of reset bits. + +:mps:tag:`fun.find-res-range-high` BTFindResRangeHigh +Exactly the same algorithm as in BTFindResRange (see .fun.find-res-range +above), but moving over the table in the opposite direction. + +:mps:tag:`fun.copy-simple-range` BTCopyRange. +Uses ACT_ON_RANGE (see .iteration above) with the obvious implementation. +Should be fast. + +:mps:tag:`fun.copy-offset-range` BTCopyOffsetRange. +Uses a simple iteration loop, reading bits with BTGet and setting them with +BTSet. Doesn't use ACT_ON_RANGE because the two ranges will not, in general, be +similarly word-aligned. + +:mps:tag:`fun.copy-invert-range` BTCopyInvertRange. +Uses ACT_ON_RANGE (see .iteration above) with the obvious implementation. +Should be fast - although there are no speed requirements. + + +Testing +------- + +:mps:tag:`test` The following tests are available / have been used during development. + +:mps:tag:`test.btcv` MMsrc!btcv.c. This is supposed to be a coverage test, intended to +execute all of the module's code in at least some minimal way. + +:mps:tag:`test.cbstest` MMsrc!cbstest.c. This was written as a test of the CBS module +(design.mps.cbs(2)). It compares the functional operation of a CBS with that +of a BT so is a good functional test of either module. + +:mps:tag:`test.mmqa.120` MMQA_test_function!210.c. This is used because it has a fair +amount of segment allocation and freeing so exercises the arena code that uses +Bit Tables. + +:mps:tag:`test.bttest` MMsrc!bttest.c. This is an interactive test that can be used to +exercise some of the BT functionality by hand. + +:mps:tag:`test.dylan` It is possible to modify Dylan so that it uses Bit Tables more +extensively. See change.mps.epcore.brisling.160181 TEST1 and TEST2.