Pharo-dev
By thread
pharo-dev@lists.pharo.org
By month
Messages by month
- ----- 2026 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
May 2012
- 115 participants
- 1500 messages
Re: [Pharo-project] [Vm-dev] Re: Plan/discussion/communication around new object format
by Igor Stasenko
Here are couple (2) of mine, highly valuable cents :)
2^20 for classes?
might be fine (or even overkill) for smalltalk systems, but could be
quite limiting for one who would like experiment and implementing a
prototype-based frameworks,
where every object is a "class" by itself.
---
8: slot size (255 => extra header word with large size)
3: odd bytes/fixed fields (odd bytes for non-pointer, fixed fields for
pointer, 7 => # fixed fields is in the class)
4 bits: format (pointers, indexable, bytes/shorts/longs/doubles
indexable, compiled method, ephemerons, weak, etc)
1: immutability
3: GC 2 mark bits. 1 forwarded bit
20: identity hash
20: class index
---
what takes most of the space in object header? right - hash!
Now, since we will have lazy become i am back to my idea of having
extra & arbitrary properties
per object.
In a nutshell, the idea is to not store hash in an object header, but
instead use just a single bit 'hash present'.
When identity hash of object is requested (via corresponding primitive)
the implementation could check if 'hash present' is set,
then if it's not there , we do a 'lazy become' of existing object to same object
copied into another place, but with hash bit set, and with extra 64-bit field,
where hash value can be stored.
So, when you requesting an identity hash for object which don't have it,
the object of from:
[header][...data.. ]
copied to new memory region with new layout:
[header][hash bits][...data..]
and old object, is of course 'corpsed' to forwarding pointer to new location.
Next step is going from holding just hash to having an arbitrary &
dynamic number of extra fields per object.
In same way, we use 1 extra bit, indicating that object having extra properties.
And when object don't have it, we lazy-become it from being
[header][...data.. ]
or
[header][hash bits][...data..]
to:
[header][hash bits][oop][...data..]
where 'oop' can be anything - instance of Array/Dictionary (depends
how language-side will decide to store extra properties of object)
This , for instance , would allow us to store extra properties for
special object formats like variable bytes or compiled methods, which
don't have the instance variables.
Not need to mention, how useful being able to attach extra properties
per object, without changing the object's class.
And , of course the freed 18 bits (20 - 2) in header can be allocated
for other purposes.
(Stef, how many bits you need for experiments? ;)
------------
About immediates zoo.
Keep in mind, that the more immediates we have, the more complex implementation
tends to be.
I would just keep 2 data types:
- integers
- floats
and third, special 'arbitrary' immediate , which seen by VM as a 60-bit value.
The interpretation of this value depends on lookup in range-table,
where developer specifying the correspondence between the value
interval and class:
[min .. max] -> class
intervals, of course, cannot overlap.
Determining a class of such immediate might be slower - O(log2(n)) at
best (where n is size of range table), but from other side,
how many different kinds of immediates you can fit into 60-bit value?
Right, it is 2^60. Much more than proposed 8 isn't? :)
And this extra cost can be mitigated completely by inline cache.
- in case of regular reference, you must fetch the object's class and
then compare it with one, stored in cache.
- in case of immediate reference, you compare immediate value with min
and max stored in cache fields.
And if value is in range, you got a cache hit, and free to proceed.
So, its just 1 extra comparison comparing to 'classic' inline cache.
And, after thinking how inline cache is organized, now you can scratch
the first my paragraph related to immediates!
We really don't need to discriminate between small integers/floats/rest!!
They could also be nothing more than just one of a range(s) defined in
our zoo of 'special' immediates!
So, at the end we will have just two kinds of references:
- zero bit == 0 -- an object pointer
- zero bit == 1 -- an immediate
Voila!.
We can have real zoo of immediates, and simple implementation to support them.
And not saying that range-table is provided by language-side, so we're
free to rearrange them at any moment.
And of course, it doesn't means that VM could not reserve some of the
ranges for own 'contracted'
immediates, like Characters, and even class reference for example.
Think about it :)
May 30, 2012
Re: [Pharo-project] [Vm-dev] Re: Plan/discussion/communication around new object format
by Eliot Miranda
On Wed, May 30, 2012 at 1:53 PM, Mariano Martinez Peck <
marianopeck(a)gmail.com> wrote:
>
>
>
> On Wed, May 30, 2012 at 10:22 PM, Eliot Miranda <eliot.miranda(a)gmail.com>wrote:
>
>>
>>
>>
>> On Wed, May 30, 2012 at 12:59 PM, Stéphane Ducasse <
>> stephane.ducasse(a)inria.fr> wrote:
>>
>>> I would like to be sure that we can have
>>> - bit for immutable objects
>>> - bits for experimenting.
>>>
>>
>> There will be quite a few. And one will be able to steal bits from the
>> class field if one needs fewer classes. I'm not absolutely sure of the
>> layout yet. But for example
>>
>> 8: slot size (255 => extra header word with large size)
>> 3: odd bytes/fixed fields (odd bytes for non-pointer, fixed fields for
>> pointer, 7 => # fixed fields is in the class)
>> 4 bits: format (pointers, indexable, bytes/shorts/longs/doubles
>> indexable, compiled method, ephemerons, weak, etc)
>> 1: immutability
>> 3: GC 2 mark bits. 1 forwarded bit
>> 20: identity hash
>>
>
> and we can make it lazy, that is, we compute it not at instantiation time
> but rather the first time the primitive is call.
>
>
>
>> 20: class index
>>
>
> This would probably work for a while. I think that it would be good to let
> an "open door" so that in the future we can just add one more word for a
> class pointer.
>
Turns out that's not such a simple change. Class indices have two
advantages. One is that they're more compact (2^20 classes is still a lot
of classes). The other is that they're constant, which has two main
benefits. First, in method caches and in-line caches the class field holds
an index and hence doesn't need to be updated by the GC. The GC no longer
has top visit send sites. Second, because they're constants both checking
for well-known classes and instantiating well-known classes can be done
without going to the specialObjectsArray. One just uses the constant. Now
undoing these optimizations to open a back-dorr is not trivial. So best
accept the benefits and exploit them to a maximum.
>
>>
>> still leaves 5 bits unused. And stealing 4 bits each from class index
>> still leaves 64k classes. So this format is simple and provides lots of
>> unused bits. The format field is a great idea as it combines a number of
>> orthogonal properties in very few bits. I don't want to include odd bytes
>> in format because I think a separate field that holds odd bytes and fixed
>> fields is better use of space. But we can gather statistics before
>> deciding.
>>
>>
>>> Stef
>>>
>>> On May 30, 2012, at 8:48 AM, Stéphane Ducasse wrote:
>>>
>>> > Hi guys
>>> >
>>> > Here is an important topic I would like to see discussed so that we
>>> see how we can improve and join forces.
>>> > May a mail discussion then a wiki for the summary would be good.
>>> >
>>> >
>>> > stef
>>> >
>>> >
>>> >
>>> > Begin forwarded message:
>>> >
>>> >> From: Eliot Miranda <eliot.miranda(a)gmail.com>
>>> >> Subject: Re: Plan/discussion/communication around new object format
>>> >> Date: May 27, 2012 10:49:54 PM GMT+02:00
>>> >> To: Stéphane Ducasse <stephane.ducasse(a)inria.fr>
>>> >>
>>> >>
>>> >>
>>> >> On Sat, May 26, 2012 at 1:46 AM, Stéphane Ducasse <
>>> stephane.ducasse(a)inria.fr> wrote:
>>> >> Hi eliot
>>> >>
>>> >> do you have a description of the new object format you want to
>>> introduce?
>>> >>
>>> >> The design is in the class comment of CogMemoryManager in the Cog
>>> VMMaker packages.
>>> >>
>>> >> Then what is your schedule?
>>> >>
>>> >> This is difficult. I have made a small start and should be able to
>>> spend time on it starting soon. I want to have it finished by early next
>>> year. But it depends on work schedules etc.
>>> >>
>>> >>
>>> >> I would like to see if we can allocate igor/esteban time before we
>>> run out of money
>>> >> to help on that important topic.
>>> >> Now the solution is unclear and I did not see any document where we
>>> can evaluate
>>> >> and plan how we can help. So do you want help on that topic? Then how
>>> can people
>>> >> contribute if any?
>>> >>
>>> >> The first thing to do is to read the design document, to see if the
>>> Pharo community thinks it is the right direction, and to review it, spot
>>> deficiencies etc. So please get those interested to read the class comment
>>> of CogMemoryManager in the latest VMMaker.oscog.
>>> >>
>>> >> Here's the current version of it:
>>> >>
>>> >> CogMemoryManager is currently a place-holder for the design of the
>>> new Cog VM's object representation and garbage collector. The goals for
>>> the GC are
>>> >>
>>> >> - efficient object representation a la Eliot Miranda's VisualWorks
>>> 64-bit object representation that uses a 64-bit header, eliminating direct
>>> class references so that all objects refer to their classes indirectly.
>>> Instead the header contains a constant class index, in a field smaller
>>> than a full pointer, These class indices are used in inline and first-level
>>> method caches, hence they do not have to be updated on GC (although they do
>>> have to be traced to be able to GC classes). Classes are held in a sparse
>>> weak table. The class table needs only to be indexed by an instance's
>>> class index in class hierarchy search, in the class primitive, and in
>>> tracing live objects in the heap. The additional header space is allocated
>>> to a much expanded identity hash field, reducing hash efficiency problems
>>> in identity collections due to the extremely small (11 bit) hash field in
>>> the old Squeak GC. The identity hash field is also a key element of the
>>> class index scheme. A class's identity hash is its index into the class
>>> table, so to create an instance of a class one merely copies its identity
>>> hash into the class index field of the new instance. This implies that
>>> when classes gain their identity hash they are entered into the class table
>>> and their identity hash is that of a previously unused index in the table.
>>> It also implies that there is a maximum number of classes in the table.
>>> At least for a few years 64k classes should be enough. A class is entered
>>> into the class table in the following operations:
>>> >> behaviorHash
>>> >> adoptInstance
>>> >> instantiate
>>> >> become (i.e. if an old class becomes a new class)
>>> >> if target class field's = to original's id hash
>>> >> and replacement's id hash is zero
>>> >> enter replacement in class table
>>> >> behaviorHash is a special version of identityHash that must be
>>> implemented in the image by any object that can function as a class (i.e.
>>> Behavior).
>>> >>
>>> >> - more immediate classes. An immediate Character class would speed
>>> up String accessing, especially for WideString, since no instatiation needs
>>> to be done on at:put: and no dereference need be done on at:. In a 32-bit
>>> system tag checking is complex since it is thought important to retain
>>> 31-bit SmallIntegers. Hence, as in current Squeak, the least significant
>>> bit set implies a SmallInteger, but Characters would likely have a tag
>>> pattern of xxx10. Hence masking with 11 results in two values for
>>> SmallInteger, xxx01 and xxx11. 30-bit characters are more than adequate
>>> for Unicode. In a 64-bit system we can use the full three bits and
>>> usefully implement an immediate Float. As in VisualWorks a functional
>>> representation takes three bits away from the exponent. Rotating to put
>>> the sign bit in the least significant non-tag bit makes expanding and
>>> contracting the 8-bit exponent to the 11-bit IEEE double exponent easy ad
>>> makes comparing negative and positive zero easier (an immediate Float is
>>> zero if its unsigned 64-bits are < 16). So the representation looks like
>>> >> | 8 bit exponent | 52 bit mantissa | sign bit | 3 tag bits |
>>> >> For details see "60-bit immediate Floats" below.
>>> >>
>>> >>
>>> >> - efficient scavenging. The current Squeak GC uses a slow
>>> pointer-reversal collector that writes every field in live objects three
>>> times in each collection, twice in the pointer-reversing heap traversal to
>>> mark live objects and once to update the pointer to its new location. A
>>> scavenger writes every field of live data twice in each collection, once as
>>> it does a block copy of the object when copying to to space, once as it
>>> traverses the live pointers in the to space objects. Of course the block
>>> copy is a relatively cheap write.
>>> >>
>>> >> - lazy become. The JIT's use of inline cacheing provides a cheap way
>>> of avoiding scanning the heap as part of a become (which is the simple
>>> approach to implementing become in a system with direct pointers). A
>>> becomeForward: on a (set of) non-zero-sized object(s) turns the object into
>>> a "corpse" or "forwarding object" whose first (non-header) word/slot is
>>> replaced by a pointer to the target of the becomeForward:. The corpse's
>>> class index is set to one that identifies corpses and, because it is a
>>> hidden class index, will always fail an inline cache test. The inline
>>> cache failure code is then responsible for following the forwarding pointer
>>> chain (these are Iliffe vectors :) ) and resolving to the actual target.
>>> We have yet to determine exactly how this is done (e.g. change the
>>> receiver register and/or stack contents and retry the send, perhaps
>>> scanning the current activation). See below on how we deal with becomes on
>>> objects with named inst vars. Note that we probably don't have to worry
>>> about zero-sized objects. These are unlikely to be passed through the FFI
>>> (there is nothing to pass :) ) and so will rarely be becommed. If they do,
>>> they can become slowly. Alternatively we can insist that objects are at
>>> least 16 bytes in size (see a8-byte alignment below) so that there will
>>> always be space for a forwarding pointer. Since none of the immediate
>>> classes can have non-immediate instances and since we allocate the
>>> immediate classes indices corresponding to their tag pattern (SmallInteger
>>> = 1, Character = 3, SmallFloat = 4?) we can use all the class indices from
>>> 0 to 7 for special uses, 0 = forward, and e.g. 1 = header-sized filler.
>>> >>
>>> >> - pinning. To support a robust and easy-to-use FFI the memory
>>> manager must support temporary pinning where individual objects can be
>>> prevented from being moved by the GC for as long as required, either by
>>> being one of an in-progress FFI call's arguments, or by having pinning
>>> asserted by a primitive (allowing objects to be passed to external code
>>> that retains a reference to the object after returning). Pinning probably
>>> implies a per-object "is-pinned" bit in the object header. Pinning will be
>>> done via lazy become; i..e an object in new space will be becommed into a
>>> pinned object in old space. We will only support pinning in old space.
>>> >>
>>> >> - efficient old space collection. An incremental collector (a la
>>> Dijkstra's three colour algorithm) collects old space, e.g. via an amount
>>> of tracing being hung off scavenges and/or old space allocations at an
>>> adaptive rate that keeps full garbage collections to a minimum. (see free
>>> space/free list below)
>>> >>
>>> >> - 8-byte alignment. It is advantageous for the FFI, for
>>> floating-point access, for object movement and for 32/64-bit
>>> compatibility to keep object sizes in units of 8 bytes. For the FFI,
>>> 8-byte alignment means passing objects to code that expects that
>>> requirement (such as modern x86 numeric processing instructions). This
>>> implies that
>>> >> - the starts of all spaces are aligned on 8-byte boundaries
>>> >> - object allocation rounds up the requested size to a multiple
>>> of 8 bytes
>>> >> - the overflow size field is also 8 bytes
>>> >> We shall probably keep the minimum object size at 16 bytes so that
>>> there is always room for a forwarding pointer. But this implies that we
>>> will need to implement an 8-byte filler to fill holes between objects > 16
>>> bytes whose length mod 16 bytes is 8 bytes and following pinned objects.
>>> We can do this using a special class index, e.g. 1, so that the method
>>> that answers the size of an object looks like, e.g.
>>> >> chunkSizeOf: oop
>>> >> <var: #oop type: #'object *'>
>>> >> ^object classIndex = 1
>>> >> ifTrue: [BaseHeaderSize]
>>> >> ifFalse: [BaseHeaderSize
>>> >> + (object slotSize = OverflowSlotSize
>>> >> ifTrue:
>>> [OverflowSizeBytes]
>>> >> ifFalse: [0])
>>> >> + (object slotSize * BytesPerSlot)]
>>> >>
>>> >> chunkStartOf: oop
>>> >> <var: #oop type: #'object *'>
>>> >> ^(self cCoerceSimple: oop to: #'char *')
>>> >> - ((object classIndex = 1
>>> >> or: [object slotSize ~= OverflowSlotSize])
>>> >> ifTrue: [0]
>>> >> ifFalse: [OverflowSizeBytes])
>>> >>
>>> >> For the moment we do not tackle the issue of heap growth and
>>> shrinkage with the ability to allocate and deallocate heap segments via
>>> memory-mapping. This technique allows space to be released back to the OS
>>> by unmapping empty segments. We may revisit this but it is not a key
>>> requirement for the first implementation.
>>> >>
>>> >> The basic approach is to use a fixed size new space and a growable
>>> old space. The new space is a classic three-space nursery a la Ungar's
>>> Generation Scavenging, a large eden for new objects and two smaller
>>> survivor spaces that exchange roles on each collection, one being the to
>>> space to which surviving objects are copied, the other being the from space
>>> of the survivors of the previous collection, i.e. the previous to space.
>>> >>
>>> >> To provide apparent pinning in new space we rely on lazy become.
>>> Since most pinned objects will be byte data and these do not require stack
>>> zone activation scanning, the overhead is simply an old space allocation
>>> and corpsing.
>>> >>
>>> >> To provide pinning in old space, large objects are implicitly pinned
>>> (because it is expensive to move large objects and, because they are both
>>> large and relatively rare, they contribute little to overall fragmentation
>>> - as in aggregates, small objects can be used to fill-in the spaces between
>>> karge objects). Hence, objects above a particular size are automatically
>>> allocated in old space, rather than new space. Small objects are pinned as
>>> per objects in new space, by asserting the pin bit, which will be set
>>> automaticaly when allocating a large object. As a last resort, or by
>>> programmer control (the fullGC primitive) old space is collected via
>>> mark-sweep (mark-compact) and so the mark phase must build the list of
>>> pinned objects around which the sweep/compact phase must carefully step.
>>> >>
>>> >> Free space in old space is organized by a free list/free tree as in
>>> Eliot's VisualWorks 5i old space allocator. There are 64 free lists,
>>> indices 1 through 63 holding blocks of space of that size, index 0 holding
>>> a semi-balanced ordered tree of free blocks, each node being the head of
>>> the list of free blocks of that size. At the start of the mark phase the
>>> free list is thrown away and the sweep phase coallesces free space and
>>> steps over pinned objects as it proceeds. We can reuse the forwarding
>>> pointer compaction scheme used in the old collector. Incremental
>>> collections merely move unmarked objects to the free lists (as well as
>>> nilling weak pointers in weak arrays and scheduling them for finalization).
>>> The occupancy of the free lists is represented by a bitmap in a 64-bit
>>> integer so that an allocation of size 63 or less can know whether there
>>> exists a free chunk of that size, but more importantly can know whether a
>>> free chunk larger than it exists in the fixed size free lists without
>>> having to search all larger free list heads.
>>> >>
>>> >> The incremental collector (a la Dijkstra's three colour algorithm)
>>> collects old space via an amount of tracing being hung off scavenges and/or
>>> old space allocations at an adaptive rate that keeps full garbage
>>> collections to a minimum. [N.B. Not sure how to do this yet. The
>>> incremental collector needs to complete a pass often enough to reclaim
>>> objects, but infrequent enough not to waste time. So some form of feedback
>>> should work. In VisualWorks tracing is broken into quanta or work where
>>> image-level code determines the size of a quantum based on how fast the
>>> machine is, and how big the heap is. This code could easily live in the
>>> VM, controllable through vmParameterAt:put:. An alternative would be to
>>> use the heartbeat to bound quanta by time. But in any case some amount of
>>> incremental collection would be done on old space allocation and
>>> scavenging, the ammount being chosen to keep pause times acceptably short,
>>> and at a rate to reclaim old space before a full GC is required, i.e. at a
>>> rate proportional to the growth in old space]. The incemental collector is
>>> a state machine, being either marking, nilling weak pointers, or freeing.
>>> If nilling weak pointers is not done atomically then there must be a read
>>> barrier in weak array at: so that reading from an old space weak array that
>>> is holding stale un-nilled references to unmarked objects. Tricks such as
>>> including the weak bit in bounds calculations can make this cheap for
>>> non-weak arrays. Alternatively nilling weak pointers can be made an atomic
>>> part of incremental collection, which can be made cheaper by maintaining
>>> the set of weak arrays (e.g. on a list).
>>> >>
>>> >> The incremental collector implies a more complex write barrier.
>>> Objects are of three colours, black, having been scanned, grey, being
>>> scanned, and white, unreached. A mark stack holds the grey objects. If
>>> the incremental collector is marking and an unmarked white object is stored
>>> into a black object then the stored object must become grey, being added to
>>> the mark stack. So the wrte barrier is essentially
>>> >> target isYoung ifFalse:
>>> >> [newValue isYoung
>>> >> ifTrue: [target isInRememberedSet ifFalse:
>>> >> [target addToRememberedSet]]
>>> "target now refers to a young object; it is a root for scavenges"
>>> >> ifFalse:
>>> >> [(target isBlack
>>> >> and: [igc marking
>>> >> and: [newValue isWhite]]) ifTrue:
>>> >> [newValue beGrey]]] "add
>>> newValue to IGC's markStack for subsequent scanning"
>>> >>
>>> >> The incremental collector does not detect already marked objects all
>>> of whose references have been overwritten by other stores (e.g. in the
>>> above if newValue overwrites the sole remaining reference to a marked
>>> object). So the incremental collector only guarantees to collect all
>>> garbage created in cycle N at the end of cycle N + 1. The cost is hence
>>> slightly worse memory density but the benefit, provided the IGC works hard
>>> enough, is the elimination of long pauses due to full garbage collections,
>>> which become actions of last resort or programmer desire.
>>> >>
>>> >> Lazy become.
>>> >>
>>> >> As described earlier the basic idea behind lazy become is to use
>>> corpses (forwarding objects) that are followed lazily during GC and inline
>>> cache miss. However, a lazy scheme cannot be used on objects with named
>>> inst vars without adding checking to all inst var accesses, which we judge
>>> too expensive. Instead, when becomming objects with named inst vars, we
>>> scan all activations in the stack zone, eagerly becomming these references,
>>> and we check for corpses when faulting in a context into the stack zone.
>>> Essentially, the invariant is that there are no references to corpses from
>>> the receiver slots of stack activations. A detail is whether we allow or
>>> forbid pinning of closure indirection vectors, or scan the entire stack of
>>> each activation. Using a special class index pun for indirection vectors
>>> is a cheap way of preventing their becomming/pinning etc. Although "don't
>>> do that" (don't attempt to pin/become indirection vectors) is also an
>>> acceptable response.
>>> >>
>>> >> 60-bit immediate Floats
>>> >> Representation for immediate doubles, only used in the 64-bit
>>> implementation. Immediate doubles have the same 52 bit mantissa as IEEE
>>> double-precision floating-point, but only have 8 bits of exponent. So
>>> they occupy just less than the middle 1/8th of the double range. They
>>> overlap the normal single-precision floats which also have 8 bit exponents,
>>> but exclude the single-precision denormals (exponent-127) and the
>>> single-precsion NaNs (exponent +127). +/- zero is just a pair of values
>>> with both exponent and mantissa 0.
>>> >> So the non-zero immediate doubles range from
>>> >> +/- 0x3800,0000,0000,0001 / 5.8774717541114d-39
>>> >> to +/- 0x47ff,ffff,ffff,ffff / 6.8056473384188d+38
>>> >> The encoded tagged form has the sign bit moved to the least
>>> significant bit, which allows for faster encode/decode because offsetting
>>> the exponent can't overflow into the sign bit and because testing for +/- 0
>>> is an unsigned compare for <= 0xf:
>>> >> msb
>>> lsb
>>> >> [8 exponent subset bits][52 mantissa bits ][1 sign bit][3 tag
>>> bits]
>>> >> So assuming the tag is 5, the tagged non-zero bit patterns are
>>> >> 0x0000,0000,0000,001[d/5]
>>> >> to 0xffff,ffff,ffff,fff[d/5]
>>> >> and +/- 0d is 0x0000,0000,0000,000[d/5]
>>> >> Encode/decode of non-zero values in machine code looks like:
>>> >> msb
>>> lsb
>>> >> Decode:
>>> [8expsubset][52mantissa][1s][3tags]
>>> >> shift away tags: [ 000
>>> ][8expsubset][52mantissa][1s]
>>> >> add exponent offset: [ 11 exponent ][52mantissa][1s]
>>> >> rot sign: [1s][ 11 exponent
>>> ][52mantissa]
>>> >>
>>> >> Encode: [1s][ 11 exponent
>>> ][52mantissa]
>>> >> rot sign: [ 11 exponent
>>> ][52mantissa][1s]
>>> >> sub exponent offset: [ 000 ][8expsubset][52 mantissa][1s]
>>> >> shift: [8expsubset][52
>>> mantissa][1s][ 000 ]
>>> >> or/add tags: [8expsubset][52mantissa][1s][3tags]
>>> >> but is slower in C because
>>> >> a) there is no rotate, and
>>> >> b) raw conversion between double and quadword must (at least in the
>>> source) move bits through memory ( quadword = *(q64 *)&doubleVariable).
>>> >>
>>> >> Issues:
>>> >> How do we avoid the Size4Bit for 64-bits? The format word encodes
>>> the number of odd bytes, but currently has only 4 bits and hence only
>>> supports odd bytes of 0 - 3. We need odd bytes of 0 - 7. But I don't like
>>> the separate Size4Bit. Best to change the VI code and have a 5 bit format?
>>> We loose one bit but save two bits (isEphemeron and isWeak (or three, if
>>> isPointers)) for a net gain of one (or two)
>>> >>
>>> >> Further, keep Squeak's format idea or go for separate bits? For
>>> 64-bits we need a 5 bit format field. This contrasts with isPointers,
>>> isWeak, isEphemeron, 3 odd size bits (or byte size).. format field is
>>> quite economical.
>>> >>
>>> >> Are class indices in inline caches strong references to classes or
>>> weak references?
>>> >> If strong then they must be scanned during GC and the methodZone must
>>> be flushed on fullGC to reclaim all classes (this looks to be a bug in the
>>> V3 Cogit).
>>> >> If weak then when the class table loses references, PICs containing
>>> freed classes must be freed and then sends to freed PICs or containing
>>> freed clases must be unlinked.
>>> >> The second approach is faster; the common case is scanning the class
>>> table, the uncommon case is freeing classes. The second approach is
>>> better; in-line caches do not prevent reclamation of classes.
>>> >>
>>> >>
>>> >> Stef
>>> >>
>>> >>
>>> >>
>>> >> --
>>> >> best,
>>> >> Eliot
>>> >>
>>> >
>>>
>>>
>>>
>>
>>
>> --
>> best,
>> Eliot
>>
>>
>>
>
>
> --
> Mariano
> http://marianopeck.wordpress.com
>
>
>
--
best,
Eliot
May 30, 2012
Re: [Pharo-project] omni browser
by Bernardo Ezequiel Contreras
on pharo 1.3, I'm using
http://jenkins.lukas-renggli.ch/job/Development/lastSuccessfulBuild/artifac…
On Wed, May 30, 2012 at 6:07 PM, Bernardo Ezequiel Contreras <
vonbecmann(a)gmail.com> wrote:
> try this:
>
> #. MetacelloConfigurationBrowser open.
> #. select ConfigurationOfOmniBrowser-*
> #. right click on "load configuration and stable version"
>
> more info,
>
> http://stackoverflow.com/questions/10518095/install-script-for-omnibrowser-…
>
> good luck.
>
>
>
> On Wed, May 30, 2012 at 5:17 PM, Milan Mimica <milan.mimica(a)gmail.com>wrote:
>
>> I don't get it. What do I do to load a working OmniBrowser in latest
>> Pharo 1.4?
>>
>>
>> --
>> Milan Mimica
>> http://sparklet.sf.net
>>
>
>
>
> --
> Bernardo E.C.
>
--
Bernardo E.C.
May 30, 2012
Re: [Pharo-project] [Vm-dev] Re: Plan/discussion/communication around new object format
by Eliot Miranda
On Wed, May 30, 2012 at 1:57 PM, Stefan Marr <smalltalk(a)stefan-marr.de>wrote:
>
> Hi Eliot:
>
> From my experience with the RoarVM, it seems to be a rather simple
> exercise to enable the VM to support a custom 'pre-header' for objects.
> That is, a constant offset in the memory that comes from the allocator,
> and is normally ignored by the GC.
>
That's a great idea. So for experimental use one simply throws a whole
word at the problem and forgets about the issue. Thanks, Stefan. That
leaves me free to focus on something fast and compact that is still
flexible. Great!
>
> That allows me to do all kind of stuff. Of course at a cost of a word per
> object, and at the cost of recompiling the VM.
> But, that should be a reasonable price to pay for someone doing research
> on these kind of things.
>
> Sometimes a few bits are just not enough, and such a pre-header gives much
> much more flexibility.
> For the people interested in that, I could dig out the details (I think, I
> did that already once on this list).
>
> Best regards
> Stefan
>
>
> On 30 May 2012, at 22:22, Eliot Miranda wrote:
>
> >
> >
> > On Wed, May 30, 2012 at 12:59 PM, Stéphane Ducasse <
> stephane.ducasse(a)inria.fr> wrote:
> > I would like to be sure that we can have
> > - bit for immutable objects
> > - bits for experimenting.
> >
> > There will be quite a few. And one will be able to steal bits from the
> class field if one needs fewer classes. I'm not absolutely sure of the
> layout yet. But for example
> >
> > 8: slot size (255 => extra header word with large size)
> > 3: odd bytes/fixed fields (odd bytes for non-pointer, fixed fields for
> pointer, 7 => # fixed fields is in the class)
> > 4 bits: format (pointers, indexable, bytes/shorts/longs/doubles
> indexable, compiled method, ephemerons, weak, etc)
> > 1: immutability
> > 3: GC 2 mark bits. 1 forwarded bit
> > 20: identity hash
> > 20: class index
> >
> > still leaves 5 bits unused. And stealing 4 bits each from class index
> still leaves 64k classes. So this format is simple and provides lots of
> unused bits. The format field is a great idea as it combines a number of
> orthogonal properties in very few bits. I don't want to include odd bytes
> in format because I think a separate field that holds odd bytes and fixed
> fields is better use of space. But we can gather statistics before
> deciding.
> >
> >
> > Stef
> >
> > On May 30, 2012, at 8:48 AM, Stéphane Ducasse wrote:
> >
> > > Hi guys
> > >
> > > Here is an important topic I would like to see discussed so that we
> see how we can improve and join forces.
> > > May a mail discussion then a wiki for the summary would be good.
> > >
> > >
> > > stef
> > >
> > >
> > >
> > > Begin forwarded message:
> > >
> > >> From: Eliot Miranda <eliot.miranda(a)gmail.com>
> > >> Subject: Re: Plan/discussion/communication around new object format
> > >> Date: May 27, 2012 10:49:54 PM GMT+02:00
> > >> To: Stéphane Ducasse <stephane.ducasse(a)inria.fr>
> > >>
> > >>
> > >>
> > >> On Sat, May 26, 2012 at 1:46 AM, Stéphane Ducasse <
> stephane.ducasse(a)inria.fr> wrote:
> > >> Hi eliot
> > >>
> > >> do you have a description of the new object format you want to
> introduce?
> > >>
> > >> The design is in the class comment of CogMemoryManager in the Cog
> VMMaker packages.
> > >>
> > >> Then what is your schedule?
> > >>
> > >> This is difficult. I have made a small start and should be able to
> spend time on it starting soon. I want to have it finished by early next
> year. But it depends on work schedules etc.
> > >>
> > >>
> > >> I would like to see if we can allocate igor/esteban time before we
> run out of money
> > >> to help on that important topic.
> > >> Now the solution is unclear and I did not see any document where we
> can evaluate
> > >> and plan how we can help. So do you want help on that topic? Then how
> can people
> > >> contribute if any?
> > >>
> > >> The first thing to do is to read the design document, to see if the
> Pharo community thinks it is the right direction, and to review it, spot
> deficiencies etc. So please get those interested to read the class comment
> of CogMemoryManager in the latest VMMaker.oscog.
> > >>
> > >> Here's the current version of it:
> > >>
> > >> CogMemoryManager is currently a place-holder for the design of the
> new Cog VM's object representation and garbage collector. The goals for
> the GC are
> > >>
> > >> - efficient object representation a la Eliot Miranda's VisualWorks
> 64-bit object representation that uses a 64-bit header, eliminating direct
> class references so that all objects refer to their classes indirectly.
> Instead the header contains a constant class index, in a field smaller
> than a full pointer, These class indices are used in inline and first-level
> method caches, hence they do not have to be updated on GC (although they do
> have to be traced to be able to GC classes). Classes are held in a sparse
> weak table. The class table needs only to be indexed by an instance's
> class index in class hierarchy search, in the class primitive, and in
> tracing live objects in the heap. The additional header space is allocated
> to a much expanded identity hash field, reducing hash efficiency problems
> in identity collections due to the extremely small (11 bit) hash field in
> the old Squeak GC. The identity hash field is also a key element of the
> class index scheme. A class's identity hash is its index into the class
> table, so to create an instance of a class one merely copies its identity
> hash into the class index field of the new instance. This implies that
> when classes gain their identity hash they are entered into the class table
> and their identity hash is that of a previously unused index in the table.
> It also implies that there is a maximum number of classes in the table.
> At least for a few years 64k classes should be enough. A class is entered
> into the class table in the following operations:
> > >> behaviorHash
> > >> adoptInstance
> > >> instantiate
> > >> become (i.e. if an old class becomes a new class)
> > >> if target class field's = to original's id hash
> > >> and replacement's id hash is zero
> > >> enter replacement in class table
> > >> behaviorHash is a special version of identityHash that must be
> implemented in the image by any object that can function as a class (i.e.
> Behavior).
> > >>
> > >> - more immediate classes. An immediate Character class would speed
> up String accessing, especially for WideString, since no instatiation needs
> to be done on at:put: and no dereference need be done on at:. In a 32-bit
> system tag checking is complex since it is thought important to retain
> 31-bit SmallIntegers. Hence, as in current Squeak, the least significant
> bit set implies a SmallInteger, but Characters would likely have a tag
> pattern of xxx10. Hence masking with 11 results in two values for
> SmallInteger, xxx01 and xxx11. 30-bit characters are more than adequate
> for Unicode. In a 64-bit system we can use the full three bits and
> usefully implement an immediate Float. As in VisualWorks a functional
> representation takes three bits away from the exponent. Rotating to put
> the sign bit in the least significant non-tag bit makes expanding and
> contracting the 8-bit exponent to the 11-bit IEEE double exponent easy ad
> makes comparing negative and positive zero easier (an immediate Float is
> zero if its unsigned 64-bits are < 16). So the representation looks like
> > >> | 8 bit exponent | 52 bit mantissa | sign bit | 3 tag bits |
> > >> For details see "60-bit immediate Floats" below.
> > >>
> > >>
> > >> - efficient scavenging. The current Squeak GC uses a slow
> pointer-reversal collector that writes every field in live objects three
> times in each collection, twice in the pointer-reversing heap traversal to
> mark live objects and once to update the pointer to its new location. A
> scavenger writes every field of live data twice in each collection, once as
> it does a block copy of the object when copying to to space, once as it
> traverses the live pointers in the to space objects. Of course the block
> copy is a relatively cheap write.
> > >>
> > >> - lazy become. The JIT's use of inline cacheing provides a cheap way
> of avoiding scanning the heap as part of a become (which is the simple
> approach to implementing become in a system with direct pointers). A
> becomeForward: on a (set of) non-zero-sized object(s) turns the object into
> a "corpse" or "forwarding object" whose first (non-header) word/slot is
> replaced by a pointer to the target of the becomeForward:. The corpse's
> class index is set to one that identifies corpses and, because it is a
> hidden class index, will always fail an inline cache test. The inline
> cache failure code is then responsible for following the forwarding pointer
> chain (these are Iliffe vectors :) ) and resolving to the actual target.
> We have yet to determine exactly how this is done (e.g. change the
> receiver register and/or stack contents and retry the send, perhaps
> scanning the current activation). See below on how we deal with becomes on
> objects with named inst vars. Note that we probably don't have to worry
> about zero-sized objects. These are unlikely to be passed through the FFI
> (there is nothing to pass :) ) and so will rarely be becommed. If they do,
> they can become slowly. Alternatively we can insist that objects are at
> least 16 bytes in size (see a8-byte alignment below) so that there will
> always be space for a forwarding pointer. Since none of the immediate
> classes can have non-immediate instances and since we allocate the
> immediate classes indices corresponding to their tag pattern (SmallInteger
> = 1, Character = 3, SmallFloat = 4?) we can use all the class indices from
> 0 to 7 for special uses, 0 = forward, and e.g. 1 = header-sized filler.
> > >>
> > >> - pinning. To support a robust and easy-to-use FFI the memory
> manager must support temporary pinning where individual objects can be
> prevented from being moved by the GC for as long as required, either by
> being one of an in-progress FFI call's arguments, or by having pinning
> asserted by a primitive (allowing objects to be passed to external code
> that retains a reference to the object after returning). Pinning probably
> implies a per-object "is-pinned" bit in the object header. Pinning will be
> done via lazy become; i..e an object in new space will be becommed into a
> pinned object in old space. We will only support pinning in old space.
> > >>
> > >> - efficient old space collection. An incremental collector (a la
> Dijkstra's three colour algorithm) collects old space, e.g. via an amount
> of tracing being hung off scavenges and/or old space allocations at an
> adaptive rate that keeps full garbage collections to a minimum. (see free
> space/free list below)
> > >>
> > >> - 8-byte alignment. It is advantageous for the FFI, for
> floating-point access, for object movement and for 32/64-bit compatibility
> to keep object sizes in units of 8 bytes. For the FFI, 8-byte alignment
> means passing objects to code that expects that requirement (such as modern
> x86 numeric processing instructions). This implies that
> > >> - the starts of all spaces are aligned on 8-byte boundaries
> > >> - object allocation rounds up the requested size to a multiple
> of 8 bytes
> > >> - the overflow size field is also 8 bytes
> > >> We shall probably keep the minimum object size at 16 bytes so that
> there is always room for a forwarding pointer. But this implies that we
> will need to implement an 8-byte filler to fill holes between objects > 16
> bytes whose length mod 16 bytes is 8 bytes and following pinned objects.
> We can do this using a special class index, e.g. 1, so that the method
> that answers the size of an object looks like, e.g.
> > >> chunkSizeOf: oop
> > >> <var: #oop type: #'object *'>
> > >> ^object classIndex = 1
> > >> ifTrue: [BaseHeaderSize]
> > >> ifFalse: [BaseHeaderSize
> > >> + (object slotSize = OverflowSlotSize
> > >> ifTrue:
> [OverflowSizeBytes]
> > >> ifFalse: [0])
> > >> + (object slotSize * BytesPerSlot)]
> > >>
> > >> chunkStartOf: oop
> > >> <var: #oop type: #'object *'>
> > >> ^(self cCoerceSimple: oop to: #'char *')
> > >> - ((object classIndex = 1
> > >> or: [object slotSize ~= OverflowSlotSize])
> > >> ifTrue: [0]
> > >> ifFalse: [OverflowSizeBytes])
> > >>
> > >> For the moment we do not tackle the issue of heap growth and
> shrinkage with the ability to allocate and deallocate heap segments via
> memory-mapping. This technique allows space to be released back to the OS
> by unmapping empty segments. We may revisit this but it is not a key
> requirement for the first implementation.
> > >>
> > >> The basic approach is to use a fixed size new space and a growable
> old space. The new space is a classic three-space nursery a la Ungar's
> Generation Scavenging, a large eden for new objects and two smaller
> survivor spaces that exchange roles on each collection, one being the to
> space to which surviving objects are copied, the other being the from space
> of the survivors of the previous collection, i.e. the previous to space.
> > >>
> > >> To provide apparent pinning in new space we rely on lazy become.
> Since most pinned objects will be byte data and these do not require stack
> zone activation scanning, the overhead is simply an old space allocation
> and corpsing.
> > >>
> > >> To provide pinning in old space, large objects are implicitly pinned
> (because it is expensive to move large objects and, because they are both
> large and relatively rare, they contribute little to overall fragmentation
> - as in aggregates, small objects can be used to fill-in the spaces between
> karge objects). Hence, objects above a particular size are automatically
> allocated in old space, rather than new space. Small objects are pinned as
> per objects in new space, by asserting the pin bit, which will be set
> automaticaly when allocating a large object. As a last resort, or by
> programmer control (the fullGC primitive) old space is collected via
> mark-sweep (mark-compact) and so the mark phase must build the list of
> pinned objects around which the sweep/compact phase must carefully step.
> > >>
> > >> Free space in old space is organized by a free list/free tree as in
> Eliot's VisualWorks 5i old space allocator. There are 64 free lists,
> indices 1 through 63 holding blocks of space of that size, index 0 holding
> a semi-balanced ordered tree of free blocks, each node being the head of
> the list of free blocks of that size. At the start of the mark phase the
> free list is thrown away and the sweep phase coallesces free space and
> steps over pinned objects as it proceeds. We can reuse the forwarding
> pointer compaction scheme used in the old collector. Incremental
> collections merely move unmarked objects to the free lists (as well as
> nilling weak pointers in weak arrays and scheduling them for finalization).
> The occupancy of the free lists is represented by a bitmap in a 64-bit
> integer so that an allocation of size 63 or less can know whether there
> exists a free chunk of that size, but more importantly can know whether a
> free chunk larger than it exists in the fixed size free lists without
> having to search all larger free list heads.
> > >>
> > >> The incremental collector (a la Dijkstra's three colour algorithm)
> collects old space via an amount of tracing being hung off scavenges and/or
> old space allocations at an adaptive rate that keeps full garbage
> collections to a minimum. [N.B. Not sure how to do this yet. The
> incremental collector needs to complete a pass often enough to reclaim
> objects, but infrequent enough not to waste time. So some form of feedback
> should work. In VisualWorks tracing is broken into quanta or work where
> image-level code determines the size of a quantum based on how fast the
> machine is, and how big the heap is. This code could easily live in the
> VM, controllable through vmParameterAt:put:. An alternative would be to
> use the heartbeat to bound quanta by time. But in any case some amount of
> incremental collection would be done on old space allocation and
> scavenging, the ammount being chosen to keep pause times acceptably short,
> and at a rate to reclaim old space before a full GC is required, i.e. at a
> rate proportional to the growth in old space]. The incemental collector is
> a state machine, being either marking, nilling weak pointers, or freeing.
> If nilling weak pointers is not done atomically then there must be a read
> barrier in weak array at: so that reading from an old space weak array that
> is holding stale un-nilled references to unmarked objects. Tricks such as
> including the weak bit in bounds calculations can make this cheap for
> non-weak arrays. Alternatively nilling weak pointers can be made an atomic
> part of incremental collection, which can be made cheaper by maintaining
> the set of weak arrays (e.g. on a list).
> > >>
> > >> The incremental collector implies a more complex write barrier.
> Objects are of three colours, black, having been scanned, grey, being
> scanned, and white, unreached. A mark stack holds the grey objects. If
> the incremental collector is marking and an unmarked white object is stored
> into a black object then the stored object must become grey, being added to
> the mark stack. So the wrte barrier is essentially
> > >> target isYoung ifFalse:
> > >> [newValue isYoung
> > >> ifTrue: [target isInRememberedSet ifFalse:
> > >> [target addToRememberedSet]]
> "target now refers to a young object; it is a root for scavenges"
> > >> ifFalse:
> > >> [(target isBlack
> > >> and: [igc marking
> > >> and: [newValue isWhite]]) ifTrue:
> > >> [newValue beGrey]]] "add
> newValue to IGC's markStack for subsequent scanning"
> > >>
> > >> The incremental collector does not detect already marked objects all
> of whose references have been overwritten by other stores (e.g. in the
> above if newValue overwrites the sole remaining reference to a marked
> object). So the incremental collector only guarantees to collect all
> garbage created in cycle N at the end of cycle N + 1. The cost is hence
> slightly worse memory density but the benefit, provided the IGC works hard
> enough, is the elimination of long pauses due to full garbage collections,
> which become actions of last resort or programmer desire.
> > >>
> > >> Lazy become.
> > >>
> > >> As described earlier the basic idea behind lazy become is to use
> corpses (forwarding objects) that are followed lazily during GC and inline
> cache miss. However, a lazy scheme cannot be used on objects with named
> inst vars without adding checking to all inst var accesses, which we judge
> too expensive. Instead, when becomming objects with named inst vars, we
> scan all activations in the stack zone, eagerly becomming these references,
> and we check for corpses when faulting in a context into the stack zone.
> Essentially, the invariant is that there are no references to corpses from
> the receiver slots of stack activations. A detail is whether we allow or
> forbid pinning of closure indirection vectors, or scan the entire stack of
> each activation. Using a special class index pun for indirection vectors
> is a cheap way of preventing their becomming/pinning etc. Although "don't
> do that" (don't attempt to pin/become indirection vectors) is also an
> acceptable response.
> > >>
> > >> 60-bit immediate Floats
> > >> Representation for immediate doubles, only used in the 64-bit
> implementation. Immediate doubles have the same 52 bit mantissa as IEEE
> double-precision floating-point, but only have 8 bits of exponent. So
> they occupy just less than the middle 1/8th of the double range. They
> overlap the normal single-precision floats which also have 8 bit exponents,
> but exclude the single-precision denormals (exponent-127) and the
> single-precsion NaNs (exponent +127). +/- zero is just a pair of values
> with both exponent and mantissa 0.
> > >> So the non-zero immediate doubles range from
> > >> +/- 0x3800,0000,0000,0001 / 5.8774717541114d-39
> > >> to +/- 0x47ff,ffff,ffff,ffff / 6.8056473384188d+38
> > >> The encoded tagged form has the sign bit moved to the least
> significant bit, which allows for faster encode/decode because offsetting
> the exponent can't overflow into the sign bit and because testing for +/- 0
> is an unsigned compare for <= 0xf:
> > >> msb
> lsb
> > >> [8 exponent subset bits][52 mantissa bits ][1 sign bit][3 tag
> bits]
> > >> So assuming the tag is 5, the tagged non-zero bit patterns are
> > >> 0x0000,0000,0000,001[d/5]
> > >> to 0xffff,ffff,ffff,fff[d/5]
> > >> and +/- 0d is 0x0000,0000,0000,000[d/5]
> > >> Encode/decode of non-zero values in machine code looks like:
> > >> msb
> lsb
> > >> Decode:
> [8expsubset][52mantissa][1s][3tags]
> > >> shift away tags: [ 000
> ][8expsubset][52mantissa][1s]
> > >> add exponent offset: [ 11 exponent ][52mantissa][1s]
> > >> rot sign: [1s][ 11 exponent
> ][52mantissa]
> > >>
> > >> Encode: [1s][ 11 exponent
> ][52mantissa]
> > >> rot sign: [ 11 exponent
> ][52mantissa][1s]
> > >> sub exponent offset: [ 000 ][8expsubset][52 mantissa][1s]
> > >> shift: [8expsubset][52
> mantissa][1s][ 000 ]
> > >> or/add tags: [8expsubset][52mantissa][1s][3tags]
> > >> but is slower in C because
> > >> a) there is no rotate, and
> > >> b) raw conversion between double and quadword must (at least in the
> source) move bits through memory ( quadword = *(q64 *)&doubleVariable).
> > >>
> > >> Issues:
> > >> How do we avoid the Size4Bit for 64-bits? The format word encodes
> the number of odd bytes, but currently has only 4 bits and hence only
> supports odd bytes of 0 - 3. We need odd bytes of 0 - 7. But I don't like
> the separate Size4Bit. Best to change the VI code and have a 5 bit format?
> We loose one bit but save two bits (isEphemeron and isWeak (or three, if
> isPointers)) for a net gain of one (or two)
> > >>
> > >> Further, keep Squeak's format idea or go for separate bits? For
> 64-bits we need a 5 bit format field. This contrasts with isPointers,
> isWeak, isEphemeron, 3 odd size bits (or byte size).. format field is
> quite economical.
> > >>
> > >> Are class indices in inline caches strong references to classes or
> weak references?
> > >> If strong then they must be scanned during GC and the methodZone must
> be flushed on fullGC to reclaim all classes (this looks to be a bug in the
> V3 Cogit).
> > >> If weak then when the class table loses references, PICs containing
> freed classes must be freed and then sends to freed PICs or containing
> freed clases must be unlinked.
> > >> The second approach is faster; the common case is scanning the class
> table, the uncommon case is freeing classes. The second approach is
> better; in-line caches do not prevent reclamation of classes.
> > >>
> > >>
> > >> Stef
> > >>
> > >>
> > >>
> > >> --
> > >> best,
> > >> Eliot
> > >>
> > >
> >
> >
> >
> >
> >
> > --
> > best,
> > Eliot
> >
>
> --
> Stefan Marr
> Software Languages Lab
> Vrije Universiteit Brussel
> Pleinlaan 2 / B-1050 Brussels / Belgium
> http://soft.vub.ac.be/~smarr
> Phone: +32 2 629 2974
> Fax: +32 2 629 3525
>
>
--
best,
Eliot
May 30, 2012
Re: [Pharo-project] omni browser
by Bernardo Ezequiel Contreras
try this:
#. MetacelloConfigurationBrowser open.
#. select ConfigurationOfOmniBrowser-*
#. right click on "load configuration and stable version"
more info,
http://stackoverflow.com/questions/10518095/install-script-for-omnibrowser-…
good luck.
On Wed, May 30, 2012 at 5:17 PM, Milan Mimica <milan.mimica(a)gmail.com>wrote:
> I don't get it. What do I do to load a working OmniBrowser in latest Pharo
> 1.4?
>
>
> --
> Milan Mimica
> http://sparklet.sf.net
>
--
Bernardo E.C.
May 30, 2012
Re: [Pharo-project] [Vm-dev] Re: Plan/discussion/communication around new object format
by Stefan Marr
Hi Eliot:
From my experience with the RoarVM, it seems to be a rather simple exercise to enable the VM to support a custom 'pre-header' for objects.
That is, a constant offset in the memory that comes from the allocator, and is normally ignored by the GC.
That allows me to do all kind of stuff. Of course at a cost of a word per object, and at the cost of recompiling the VM.
But, that should be a reasonable price to pay for someone doing research on these kind of things.
Sometimes a few bits are just not enough, and such a pre-header gives much much more flexibility.
For the people interested in that, I could dig out the details (I think, I did that already once on this list).
Best regards
Stefan
On 30 May 2012, at 22:22, Eliot Miranda wrote:
>
>
> On Wed, May 30, 2012 at 12:59 PM, Stéphane Ducasse <stephane.ducasse(a)inria.fr> wrote:
> I would like to be sure that we can have
> - bit for immutable objects
> - bits for experimenting.
>
> There will be quite a few. And one will be able to steal bits from the class field if one needs fewer classes. I'm not absolutely sure of the layout yet. But for example
>
> 8: slot size (255 => extra header word with large size)
> 3: odd bytes/fixed fields (odd bytes for non-pointer, fixed fields for pointer, 7 => # fixed fields is in the class)
> 4 bits: format (pointers, indexable, bytes/shorts/longs/doubles indexable, compiled method, ephemerons, weak, etc)
> 1: immutability
> 3: GC 2 mark bits. 1 forwarded bit
> 20: identity hash
> 20: class index
>
> still leaves 5 bits unused. And stealing 4 bits each from class index still leaves 64k classes. So this format is simple and provides lots of unused bits. The format field is a great idea as it combines a number of orthogonal properties in very few bits. I don't want to include odd bytes in format because I think a separate field that holds odd bytes and fixed fields is better use of space. But we can gather statistics before deciding.
>
>
> Stef
>
> On May 30, 2012, at 8:48 AM, Stéphane Ducasse wrote:
>
> > Hi guys
> >
> > Here is an important topic I would like to see discussed so that we see how we can improve and join forces.
> > May a mail discussion then a wiki for the summary would be good.
> >
> >
> > stef
> >
> >
> >
> > Begin forwarded message:
> >
> >> From: Eliot Miranda <eliot.miranda(a)gmail.com>
> >> Subject: Re: Plan/discussion/communication around new object format
> >> Date: May 27, 2012 10:49:54 PM GMT+02:00
> >> To: Stéphane Ducasse <stephane.ducasse(a)inria.fr>
> >>
> >>
> >>
> >> On Sat, May 26, 2012 at 1:46 AM, Stéphane Ducasse <stephane.ducasse(a)inria.fr> wrote:
> >> Hi eliot
> >>
> >> do you have a description of the new object format you want to introduce?
> >>
> >> The design is in the class comment of CogMemoryManager in the Cog VMMaker packages.
> >>
> >> Then what is your schedule?
> >>
> >> This is difficult. I have made a small start and should be able to spend time on it starting soon. I want to have it finished by early next year. But it depends on work schedules etc.
> >>
> >>
> >> I would like to see if we can allocate igor/esteban time before we run out of money
> >> to help on that important topic.
> >> Now the solution is unclear and I did not see any document where we can evaluate
> >> and plan how we can help. So do you want help on that topic? Then how can people
> >> contribute if any?
> >>
> >> The first thing to do is to read the design document, to see if the Pharo community thinks it is the right direction, and to review it, spot deficiencies etc. So please get those interested to read the class comment of CogMemoryManager in the latest VMMaker.oscog.
> >>
> >> Here's the current version of it:
> >>
> >> CogMemoryManager is currently a place-holder for the design of the new Cog VM's object representation and garbage collector. The goals for the GC are
> >>
> >> - efficient object representation a la Eliot Miranda's VisualWorks 64-bit object representation that uses a 64-bit header, eliminating direct class references so that all objects refer to their classes indirectly. Instead the header contains a constant class index, in a field smaller than a full pointer, These class indices are used in inline and first-level method caches, hence they do not have to be updated on GC (although they do have to be traced to be able to GC classes). Classes are held in a sparse weak table. The class table needs only to be indexed by an instance's class index in class hierarchy search, in the class primitive, and in tracing live objects in the heap. The additional header space is allocated to a much expanded identity hash field, reducing hash efficiency problems in identity collections due to the extremely small (11 bit) hash field in the old Squeak GC. The identity hash field is also a key element of the class index scheme. A class's identity hash is its index into the class table, so to create an instance of a class one merely copies its identity hash into the class index field of the new instance. This implies that when classes gain their identity hash they are entered into the class table and their identity hash is that of a previously unused index in the table. It also implies that there is a maximum number of classes in the table. At least for a few years 64k classes should be enough. A class is entered into the class table in the following operations:
> >> behaviorHash
> >> adoptInstance
> >> instantiate
> >> become (i.e. if an old class becomes a new class)
> >> if target class field's = to original's id hash
> >> and replacement's id hash is zero
> >> enter replacement in class table
> >> behaviorHash is a special version of identityHash that must be implemented in the image by any object that can function as a class (i.e. Behavior).
> >>
> >> - more immediate classes. An immediate Character class would speed up String accessing, especially for WideString, since no instatiation needs to be done on at:put: and no dereference need be done on at:. In a 32-bit system tag checking is complex since it is thought important to retain 31-bit SmallIntegers. Hence, as in current Squeak, the least significant bit set implies a SmallInteger, but Characters would likely have a tag pattern of xxx10. Hence masking with 11 results in two values for SmallInteger, xxx01 and xxx11. 30-bit characters are more than adequate for Unicode. In a 64-bit system we can use the full three bits and usefully implement an immediate Float. As in VisualWorks a functional representation takes three bits away from the exponent. Rotating to put the sign bit in the least significant non-tag bit makes expanding and contracting the 8-bit exponent to the 11-bit IEEE double exponent easy ad makes comparing negative and positive zero easier (an immediate Float is zero if its unsigned 64-bits are < 16). So the representation looks like
> >> | 8 bit exponent | 52 bit mantissa | sign bit | 3 tag bits |
> >> For details see "60-bit immediate Floats" below.
> >>
> >>
> >> - efficient scavenging. The current Squeak GC uses a slow pointer-reversal collector that writes every field in live objects three times in each collection, twice in the pointer-reversing heap traversal to mark live objects and once to update the pointer to its new location. A scavenger writes every field of live data twice in each collection, once as it does a block copy of the object when copying to to space, once as it traverses the live pointers in the to space objects. Of course the block copy is a relatively cheap write.
> >>
> >> - lazy become. The JIT's use of inline cacheing provides a cheap way of avoiding scanning the heap as part of a become (which is the simple approach to implementing become in a system with direct pointers). A becomeForward: on a (set of) non-zero-sized object(s) turns the object into a "corpse" or "forwarding object" whose first (non-header) word/slot is replaced by a pointer to the target of the becomeForward:. The corpse's class index is set to one that identifies corpses and, because it is a hidden class index, will always fail an inline cache test. The inline cache failure code is then responsible for following the forwarding pointer chain (these are Iliffe vectors :) ) and resolving to the actual target. We have yet to determine exactly how this is done (e.g. change the receiver register and/or stack contents and retry the send, perhaps scanning the current activation). See below on how we deal with becomes on objects with named inst vars. Note that we probably don't have to worry about zero-sized objects. These are unlikely to be passed through the FFI (there is nothing to pass :) ) and so will rarely be becommed. If they do, they can become slowly. Alternatively we can insist that objects are at least 16 bytes in size (see a8-byte alignment below) so that there will always be space for a forwarding pointer. Since none of the immediate classes can have non-immediate instances and since we allocate the immediate classes indices corresponding to their tag pattern (SmallInteger = 1, Character = 3, SmallFloat = 4?) we can use all the class indices from 0 to 7 for special uses, 0 = forward, and e.g. 1 = header-sized filler.
> >>
> >> - pinning. To support a robust and easy-to-use FFI the memory manager must support temporary pinning where individual objects can be prevented from being moved by the GC for as long as required, either by being one of an in-progress FFI call's arguments, or by having pinning asserted by a primitive (allowing objects to be passed to external code that retains a reference to the object after returning). Pinning probably implies a per-object "is-pinned" bit in the object header. Pinning will be done via lazy become; i..e an object in new space will be becommed into a pinned object in old space. We will only support pinning in old space.
> >>
> >> - efficient old space collection. An incremental collector (a la Dijkstra's three colour algorithm) collects old space, e.g. via an amount of tracing being hung off scavenges and/or old space allocations at an adaptive rate that keeps full garbage collections to a minimum. (see free space/free list below)
> >>
> >> - 8-byte alignment. It is advantageous for the FFI, for floating-point access, for object movement and for 32/64-bit compatibility to keep object sizes in units of 8 bytes. For the FFI, 8-byte alignment means passing objects to code that expects that requirement (such as modern x86 numeric processing instructions). This implies that
> >> - the starts of all spaces are aligned on 8-byte boundaries
> >> - object allocation rounds up the requested size to a multiple of 8 bytes
> >> - the overflow size field is also 8 bytes
> >> We shall probably keep the minimum object size at 16 bytes so that there is always room for a forwarding pointer. But this implies that we will need to implement an 8-byte filler to fill holes between objects > 16 bytes whose length mod 16 bytes is 8 bytes and following pinned objects. We can do this using a special class index, e.g. 1, so that the method that answers the size of an object looks like, e.g.
> >> chunkSizeOf: oop
> >> <var: #oop type: #'object *'>
> >> ^object classIndex = 1
> >> ifTrue: [BaseHeaderSize]
> >> ifFalse: [BaseHeaderSize
> >> + (object slotSize = OverflowSlotSize
> >> ifTrue: [OverflowSizeBytes]
> >> ifFalse: [0])
> >> + (object slotSize * BytesPerSlot)]
> >>
> >> chunkStartOf: oop
> >> <var: #oop type: #'object *'>
> >> ^(self cCoerceSimple: oop to: #'char *')
> >> - ((object classIndex = 1
> >> or: [object slotSize ~= OverflowSlotSize])
> >> ifTrue: [0]
> >> ifFalse: [OverflowSizeBytes])
> >>
> >> For the moment we do not tackle the issue of heap growth and shrinkage with the ability to allocate and deallocate heap segments via memory-mapping. This technique allows space to be released back to the OS by unmapping empty segments. We may revisit this but it is not a key requirement for the first implementation.
> >>
> >> The basic approach is to use a fixed size new space and a growable old space. The new space is a classic three-space nursery a la Ungar's Generation Scavenging, a large eden for new objects and two smaller survivor spaces that exchange roles on each collection, one being the to space to which surviving objects are copied, the other being the from space of the survivors of the previous collection, i.e. the previous to space.
> >>
> >> To provide apparent pinning in new space we rely on lazy become. Since most pinned objects will be byte data and these do not require stack zone activation scanning, the overhead is simply an old space allocation and corpsing.
> >>
> >> To provide pinning in old space, large objects are implicitly pinned (because it is expensive to move large objects and, because they are both large and relatively rare, they contribute little to overall fragmentation - as in aggregates, small objects can be used to fill-in the spaces between karge objects). Hence, objects above a particular size are automatically allocated in old space, rather than new space. Small objects are pinned as per objects in new space, by asserting the pin bit, which will be set automaticaly when allocating a large object. As a last resort, or by programmer control (the fullGC primitive) old space is collected via mark-sweep (mark-compact) and so the mark phase must build the list of pinned objects around which the sweep/compact phase must carefully step.
> >>
> >> Free space in old space is organized by a free list/free tree as in Eliot's VisualWorks 5i old space allocator. There are 64 free lists, indices 1 through 63 holding blocks of space of that size, index 0 holding a semi-balanced ordered tree of free blocks, each node being the head of the list of free blocks of that size. At the start of the mark phase the free list is thrown away and the sweep phase coallesces free space and steps over pinned objects as it proceeds. We can reuse the forwarding pointer compaction scheme used in the old collector. Incremental collections merely move unmarked objects to the free lists (as well as nilling weak pointers in weak arrays and scheduling them for finalization). The occupancy of the free lists is represented by a bitmap in a 64-bit integer so that an allocation of size 63 or less can know whether there exists a free chunk of that size, but more importantly can know whether a free chunk larger than it exists in the fixed size free lists without having to search all larger free list heads.
> >>
> >> The incremental collector (a la Dijkstra's three colour algorithm) collects old space via an amount of tracing being hung off scavenges and/or old space allocations at an adaptive rate that keeps full garbage collections to a minimum. [N.B. Not sure how to do this yet. The incremental collector needs to complete a pass often enough to reclaim objects, but infrequent enough not to waste time. So some form of feedback should work. In VisualWorks tracing is broken into quanta or work where image-level code determines the size of a quantum based on how fast the machine is, and how big the heap is. This code could easily live in the VM, controllable through vmParameterAt:put:. An alternative would be to use the heartbeat to bound quanta by time. But in any case some amount of incremental collection would be done on old space allocation and scavenging, the ammount being chosen to keep pause times acceptably short, and at a rate to reclaim old space before a full GC is required, i.e. at a rate proportional to the growth in old space]. The incemental collector is a state machine, being either marking, nilling weak pointers, or freeing. If nilling weak pointers is not done atomically then there must be a read barrier in weak array at: so that reading from an old space weak array that is holding stale un-nilled references to unmarked objects. Tricks such as including the weak bit in bounds calculations can make this cheap for non-weak arrays. Alternatively nilling weak pointers can be made an atomic part of incremental collection, which can be made cheaper by maintaining the set of weak arrays (e.g. on a list).
> >>
> >> The incremental collector implies a more complex write barrier. Objects are of three colours, black, having been scanned, grey, being scanned, and white, unreached. A mark stack holds the grey objects. If the incremental collector is marking and an unmarked white object is stored into a black object then the stored object must become grey, being added to the mark stack. So the wrte barrier is essentially
> >> target isYoung ifFalse:
> >> [newValue isYoung
> >> ifTrue: [target isInRememberedSet ifFalse:
> >> [target addToRememberedSet]] "target now refers to a young object; it is a root for scavenges"
> >> ifFalse:
> >> [(target isBlack
> >> and: [igc marking
> >> and: [newValue isWhite]]) ifTrue:
> >> [newValue beGrey]]] "add newValue to IGC's markStack for subsequent scanning"
> >>
> >> The incremental collector does not detect already marked objects all of whose references have been overwritten by other stores (e.g. in the above if newValue overwrites the sole remaining reference to a marked object). So the incremental collector only guarantees to collect all garbage created in cycle N at the end of cycle N + 1. The cost is hence slightly worse memory density but the benefit, provided the IGC works hard enough, is the elimination of long pauses due to full garbage collections, which become actions of last resort or programmer desire.
> >>
> >> Lazy become.
> >>
> >> As described earlier the basic idea behind lazy become is to use corpses (forwarding objects) that are followed lazily during GC and inline cache miss. However, a lazy scheme cannot be used on objects with named inst vars without adding checking to all inst var accesses, which we judge too expensive. Instead, when becomming objects with named inst vars, we scan all activations in the stack zone, eagerly becomming these references, and we check for corpses when faulting in a context into the stack zone. Essentially, the invariant is that there are no references to corpses from the receiver slots of stack activations. A detail is whether we allow or forbid pinning of closure indirection vectors, or scan the entire stack of each activation. Using a special class index pun for indirection vectors is a cheap way of preventing their becomming/pinning etc. Although "don't do that" (don't attempt to pin/become indirection vectors) is also an acceptable response.
> >>
> >> 60-bit immediate Floats
> >> Representation for immediate doubles, only used in the 64-bit implementation. Immediate doubles have the same 52 bit mantissa as IEEE double-precision floating-point, but only have 8 bits of exponent. So they occupy just less than the middle 1/8th of the double range. They overlap the normal single-precision floats which also have 8 bit exponents, but exclude the single-precision denormals (exponent-127) and the single-precsion NaNs (exponent +127). +/- zero is just a pair of values with both exponent and mantissa 0.
> >> So the non-zero immediate doubles range from
> >> +/- 0x3800,0000,0000,0001 / 5.8774717541114d-39
> >> to +/- 0x47ff,ffff,ffff,ffff / 6.8056473384188d+38
> >> The encoded tagged form has the sign bit moved to the least significant bit, which allows for faster encode/decode because offsetting the exponent can't overflow into the sign bit and because testing for +/- 0 is an unsigned compare for <= 0xf:
> >> msb lsb
> >> [8 exponent subset bits][52 mantissa bits ][1 sign bit][3 tag bits]
> >> So assuming the tag is 5, the tagged non-zero bit patterns are
> >> 0x0000,0000,0000,001[d/5]
> >> to 0xffff,ffff,ffff,fff[d/5]
> >> and +/- 0d is 0x0000,0000,0000,000[d/5]
> >> Encode/decode of non-zero values in machine code looks like:
> >> msb lsb
> >> Decode: [8expsubset][52mantissa][1s][3tags]
> >> shift away tags: [ 000 ][8expsubset][52mantissa][1s]
> >> add exponent offset: [ 11 exponent ][52mantissa][1s]
> >> rot sign: [1s][ 11 exponent ][52mantissa]
> >>
> >> Encode: [1s][ 11 exponent ][52mantissa]
> >> rot sign: [ 11 exponent ][52mantissa][1s]
> >> sub exponent offset: [ 000 ][8expsubset][52 mantissa][1s]
> >> shift: [8expsubset][52 mantissa][1s][ 000 ]
> >> or/add tags: [8expsubset][52mantissa][1s][3tags]
> >> but is slower in C because
> >> a) there is no rotate, and
> >> b) raw conversion between double and quadword must (at least in the source) move bits through memory ( quadword = *(q64 *)&doubleVariable).
> >>
> >> Issues:
> >> How do we avoid the Size4Bit for 64-bits? The format word encodes the number of odd bytes, but currently has only 4 bits and hence only supports odd bytes of 0 - 3. We need odd bytes of 0 - 7. But I don't like the separate Size4Bit. Best to change the VI code and have a 5 bit format? We loose one bit but save two bits (isEphemeron and isWeak (or three, if isPointers)) for a net gain of one (or two)
> >>
> >> Further, keep Squeak's format idea or go for separate bits? For 64-bits we need a 5 bit format field. This contrasts with isPointers, isWeak, isEphemeron, 3 odd size bits (or byte size).. format field is quite economical.
> >>
> >> Are class indices in inline caches strong references to classes or weak references?
> >> If strong then they must be scanned during GC and the methodZone must be flushed on fullGC to reclaim all classes (this looks to be a bug in the V3 Cogit).
> >> If weak then when the class table loses references, PICs containing freed classes must be freed and then sends to freed PICs or containing freed clases must be unlinked.
> >> The second approach is faster; the common case is scanning the class table, the uncommon case is freeing classes. The second approach is better; in-line caches do not prevent reclamation of classes.
> >>
> >>
> >> Stef
> >>
> >>
> >>
> >> --
> >> best,
> >> Eliot
> >>
> >
>
>
>
>
>
> --
> best,
> Eliot
>
--
Stefan Marr
Software Languages Lab
Vrije Universiteit Brussel
Pleinlaan 2 / B-1050 Brussels / Belgium
http://soft.vub.ac.be/~smarr
Phone: +32 2 629 2974
Fax: +32 2 629 3525
May 30, 2012
Re: [Pharo-project] [Vm-dev] Re: Plan/discussion/communication around new object format
by Mariano Martinez Peck
On Wed, May 30, 2012 at 10:22 PM, Eliot Miranda <eliot.miranda(a)gmail.com>wrote:
>
>
>
> On Wed, May 30, 2012 at 12:59 PM, Stéphane Ducasse <
> stephane.ducasse(a)inria.fr> wrote:
>
>> I would like to be sure that we can have
>> - bit for immutable objects
>> - bits for experimenting.
>>
>
> There will be quite a few. And one will be able to steal bits from the
> class field if one needs fewer classes. I'm not absolutely sure of the
> layout yet. But for example
>
> 8: slot size (255 => extra header word with large size)
> 3: odd bytes/fixed fields (odd bytes for non-pointer, fixed fields for
> pointer, 7 => # fixed fields is in the class)
> 4 bits: format (pointers, indexable, bytes/shorts/longs/doubles indexable,
> compiled method, ephemerons, weak, etc)
> 1: immutability
> 3: GC 2 mark bits. 1 forwarded bit
> 20: identity hash
>
and we can make it lazy, that is, we compute it not at instantiation time
but rather the first time the primitive is call.
> 20: class index
>
This would probably work for a while. I think that it would be good to let
an "open door" so that in the future we can just add one more word for a
class pointer.
>
> still leaves 5 bits unused. And stealing 4 bits each from class index
> still leaves 64k classes. So this format is simple and provides lots of
> unused bits. The format field is a great idea as it combines a number of
> orthogonal properties in very few bits. I don't want to include odd bytes
> in format because I think a separate field that holds odd bytes and fixed
> fields is better use of space. But we can gather statistics before
> deciding.
>
>
>> Stef
>>
>> On May 30, 2012, at 8:48 AM, Stéphane Ducasse wrote:
>>
>> > Hi guys
>> >
>> > Here is an important topic I would like to see discussed so that we see
>> how we can improve and join forces.
>> > May a mail discussion then a wiki for the summary would be good.
>> >
>> >
>> > stef
>> >
>> >
>> >
>> > Begin forwarded message:
>> >
>> >> From: Eliot Miranda <eliot.miranda(a)gmail.com>
>> >> Subject: Re: Plan/discussion/communication around new object format
>> >> Date: May 27, 2012 10:49:54 PM GMT+02:00
>> >> To: Stéphane Ducasse <stephane.ducasse(a)inria.fr>
>> >>
>> >>
>> >>
>> >> On Sat, May 26, 2012 at 1:46 AM, Stéphane Ducasse <
>> stephane.ducasse(a)inria.fr> wrote:
>> >> Hi eliot
>> >>
>> >> do you have a description of the new object format you want to
>> introduce?
>> >>
>> >> The design is in the class comment of CogMemoryManager in the Cog
>> VMMaker packages.
>> >>
>> >> Then what is your schedule?
>> >>
>> >> This is difficult. I have made a small start and should be able to
>> spend time on it starting soon. I want to have it finished by early next
>> year. But it depends on work schedules etc.
>> >>
>> >>
>> >> I would like to see if we can allocate igor/esteban time before we run
>> out of money
>> >> to help on that important topic.
>> >> Now the solution is unclear and I did not see any document where we
>> can evaluate
>> >> and plan how we can help. So do you want help on that topic? Then how
>> can people
>> >> contribute if any?
>> >>
>> >> The first thing to do is to read the design document, to see if the
>> Pharo community thinks it is the right direction, and to review it, spot
>> deficiencies etc. So please get those interested to read the class comment
>> of CogMemoryManager in the latest VMMaker.oscog.
>> >>
>> >> Here's the current version of it:
>> >>
>> >> CogMemoryManager is currently a place-holder for the design of the new
>> Cog VM's object representation and garbage collector. The goals for the GC
>> are
>> >>
>> >> - efficient object representation a la Eliot Miranda's VisualWorks
>> 64-bit object representation that uses a 64-bit header, eliminating direct
>> class references so that all objects refer to their classes indirectly.
>> Instead the header contains a constant class index, in a field smaller
>> than a full pointer, These class indices are used in inline and first-level
>> method caches, hence they do not have to be updated on GC (although they do
>> have to be traced to be able to GC classes). Classes are held in a sparse
>> weak table. The class table needs only to be indexed by an instance's
>> class index in class hierarchy search, in the class primitive, and in
>> tracing live objects in the heap. The additional header space is allocated
>> to a much expanded identity hash field, reducing hash efficiency problems
>> in identity collections due to the extremely small (11 bit) hash field in
>> the old Squeak GC. The identity hash field is also a key element of the
>> class index scheme. A class's identity hash is its index into the class
>> table, so to create an instance of a class one merely copies its identity
>> hash into the class index field of the new instance. This implies that
>> when classes gain their identity hash they are entered into the class table
>> and their identity hash is that of a previously unused index in the table.
>> It also implies that there is a maximum number of classes in the table.
>> At least for a few years 64k classes should be enough. A class is entered
>> into the class table in the following operations:
>> >> behaviorHash
>> >> adoptInstance
>> >> instantiate
>> >> become (i.e. if an old class becomes a new class)
>> >> if target class field's = to original's id hash
>> >> and replacement's id hash is zero
>> >> enter replacement in class table
>> >> behaviorHash is a special version of identityHash that must be
>> implemented in the image by any object that can function as a class (i.e.
>> Behavior).
>> >>
>> >> - more immediate classes. An immediate Character class would speed up
>> String accessing, especially for WideString, since no instatiation needs to
>> be done on at:put: and no dereference need be done on at:. In a 32-bit
>> system tag checking is complex since it is thought important to retain
>> 31-bit SmallIntegers. Hence, as in current Squeak, the least significant
>> bit set implies a SmallInteger, but Characters would likely have a tag
>> pattern of xxx10. Hence masking with 11 results in two values for
>> SmallInteger, xxx01 and xxx11. 30-bit characters are more than adequate
>> for Unicode. In a 64-bit system we can use the full three bits and
>> usefully implement an immediate Float. As in VisualWorks a functional
>> representation takes three bits away from the exponent. Rotating to put
>> the sign bit in the least significant non-tag bit makes expanding and
>> contracting the 8-bit exponent to the 11-bit IEEE double exponent easy ad
>> makes comparing negative and positive zero easier (an immediate Float is
>> zero if its unsigned 64-bits are < 16). So the representation looks like
>> >> | 8 bit exponent | 52 bit mantissa | sign bit | 3 tag bits |
>> >> For details see "60-bit immediate Floats" below.
>> >>
>> >>
>> >> - efficient scavenging. The current Squeak GC uses a slow
>> pointer-reversal collector that writes every field in live objects three
>> times in each collection, twice in the pointer-reversing heap traversal to
>> mark live objects and once to update the pointer to its new location. A
>> scavenger writes every field of live data twice in each collection, once as
>> it does a block copy of the object when copying to to space, once as it
>> traverses the live pointers in the to space objects. Of course the block
>> copy is a relatively cheap write.
>> >>
>> >> - lazy become. The JIT's use of inline cacheing provides a cheap way
>> of avoiding scanning the heap as part of a become (which is the simple
>> approach to implementing become in a system with direct pointers). A
>> becomeForward: on a (set of) non-zero-sized object(s) turns the object into
>> a "corpse" or "forwarding object" whose first (non-header) word/slot is
>> replaced by a pointer to the target of the becomeForward:. The corpse's
>> class index is set to one that identifies corpses and, because it is a
>> hidden class index, will always fail an inline cache test. The inline
>> cache failure code is then responsible for following the forwarding pointer
>> chain (these are Iliffe vectors :) ) and resolving to the actual target.
>> We have yet to determine exactly how this is done (e.g. change the
>> receiver register and/or stack contents and retry the send, perhaps
>> scanning the current activation). See below on how we deal with becomes on
>> objects with named inst vars. Note that we probably don't have to worry
>> about zero-sized objects. These are unlikely to be passed through the FFI
>> (there is nothing to pass :) ) and so will rarely be becommed. If they do,
>> they can become slowly. Alternatively we can insist that objects are at
>> least 16 bytes in size (see a8-byte alignment below) so that there will
>> always be space for a forwarding pointer. Since none of the immediate
>> classes can have non-immediate instances and since we allocate the
>> immediate classes indices corresponding to their tag pattern (SmallInteger
>> = 1, Character = 3, SmallFloat = 4?) we can use all the class indices from
>> 0 to 7 for special uses, 0 = forward, and e.g. 1 = header-sized filler.
>> >>
>> >> - pinning. To support a robust and easy-to-use FFI the memory manager
>> must support temporary pinning where individual objects can be prevented
>> from being moved by the GC for as long as required, either by being one of
>> an in-progress FFI call's arguments, or by having pinning asserted by a
>> primitive (allowing objects to be passed to external code that retains a
>> reference to the object after returning). Pinning probably implies a
>> per-object "is-pinned" bit in the object header. Pinning will be done via
>> lazy become; i..e an object in new space will be becommed into a pinned
>> object in old space. We will only support pinning in old space.
>> >>
>> >> - efficient old space collection. An incremental collector (a la
>> Dijkstra's three colour algorithm) collects old space, e.g. via an amount
>> of tracing being hung off scavenges and/or old space allocations at an
>> adaptive rate that keeps full garbage collections to a minimum. (see free
>> space/free list below)
>> >>
>> >> - 8-byte alignment. It is advantageous for the FFI, for
>> floating-point access, for object movement and for 32/64-bit
>> compatibility to keep object sizes in units of 8 bytes. For the FFI,
>> 8-byte alignment means passing objects to code that expects that
>> requirement (such as modern x86 numeric processing instructions). This
>> implies that
>> >> - the starts of all spaces are aligned on 8-byte boundaries
>> >> - object allocation rounds up the requested size to a multiple of
>> 8 bytes
>> >> - the overflow size field is also 8 bytes
>> >> We shall probably keep the minimum object size at 16 bytes so that
>> there is always room for a forwarding pointer. But this implies that we
>> will need to implement an 8-byte filler to fill holes between objects > 16
>> bytes whose length mod 16 bytes is 8 bytes and following pinned objects.
>> We can do this using a special class index, e.g. 1, so that the method
>> that answers the size of an object looks like, e.g.
>> >> chunkSizeOf: oop
>> >> <var: #oop type: #'object *'>
>> >> ^object classIndex = 1
>> >> ifTrue: [BaseHeaderSize]
>> >> ifFalse: [BaseHeaderSize
>> >> + (object slotSize = OverflowSlotSize
>> >> ifTrue:
>> [OverflowSizeBytes]
>> >> ifFalse: [0])
>> >> + (object slotSize * BytesPerSlot)]
>> >>
>> >> chunkStartOf: oop
>> >> <var: #oop type: #'object *'>
>> >> ^(self cCoerceSimple: oop to: #'char *')
>> >> - ((object classIndex = 1
>> >> or: [object slotSize ~= OverflowSlotSize])
>> >> ifTrue: [0]
>> >> ifFalse: [OverflowSizeBytes])
>> >>
>> >> For the moment we do not tackle the issue of heap growth and shrinkage
>> with the ability to allocate and deallocate heap segments via
>> memory-mapping. This technique allows space to be released back to the OS
>> by unmapping empty segments. We may revisit this but it is not a key
>> requirement for the first implementation.
>> >>
>> >> The basic approach is to use a fixed size new space and a growable old
>> space. The new space is a classic three-space nursery a la Ungar's
>> Generation Scavenging, a large eden for new objects and two smaller
>> survivor spaces that exchange roles on each collection, one being the to
>> space to which surviving objects are copied, the other being the from space
>> of the survivors of the previous collection, i.e. the previous to space.
>> >>
>> >> To provide apparent pinning in new space we rely on lazy become.
>> Since most pinned objects will be byte data and these do not require stack
>> zone activation scanning, the overhead is simply an old space allocation
>> and corpsing.
>> >>
>> >> To provide pinning in old space, large objects are implicitly pinned
>> (because it is expensive to move large objects and, because they are both
>> large and relatively rare, they contribute little to overall fragmentation
>> - as in aggregates, small objects can be used to fill-in the spaces between
>> karge objects). Hence, objects above a particular size are automatically
>> allocated in old space, rather than new space. Small objects are pinned as
>> per objects in new space, by asserting the pin bit, which will be set
>> automaticaly when allocating a large object. As a last resort, or by
>> programmer control (the fullGC primitive) old space is collected via
>> mark-sweep (mark-compact) and so the mark phase must build the list of
>> pinned objects around which the sweep/compact phase must carefully step.
>> >>
>> >> Free space in old space is organized by a free list/free tree as in
>> Eliot's VisualWorks 5i old space allocator. There are 64 free lists,
>> indices 1 through 63 holding blocks of space of that size, index 0 holding
>> a semi-balanced ordered tree of free blocks, each node being the head of
>> the list of free blocks of that size. At the start of the mark phase the
>> free list is thrown away and the sweep phase coallesces free space and
>> steps over pinned objects as it proceeds. We can reuse the forwarding
>> pointer compaction scheme used in the old collector. Incremental
>> collections merely move unmarked objects to the free lists (as well as
>> nilling weak pointers in weak arrays and scheduling them for finalization).
>> The occupancy of the free lists is represented by a bitmap in a 64-bit
>> integer so that an allocation of size 63 or less can know whether there
>> exists a free chunk of that size, but more importantly can know whether a
>> free chunk larger than it exists in the fixed size free lists without
>> having to search all larger free list heads.
>> >>
>> >> The incremental collector (a la Dijkstra's three colour algorithm)
>> collects old space via an amount of tracing being hung off scavenges and/or
>> old space allocations at an adaptive rate that keeps full garbage
>> collections to a minimum. [N.B. Not sure how to do this yet. The
>> incremental collector needs to complete a pass often enough to reclaim
>> objects, but infrequent enough not to waste time. So some form of feedback
>> should work. In VisualWorks tracing is broken into quanta or work where
>> image-level code determines the size of a quantum based on how fast the
>> machine is, and how big the heap is. This code could easily live in the
>> VM, controllable through vmParameterAt:put:. An alternative would be to
>> use the heartbeat to bound quanta by time. But in any case some amount of
>> incremental collection would be done on old space allocation and
>> scavenging, the ammount being chosen to keep pause times acceptably short,
>> and at a rate to reclaim old space before a full GC is required, i.e. at a
>> rate proportional to the growth in old space]. The incemental collector is
>> a state machine, being either marking, nilling weak pointers, or freeing.
>> If nilling weak pointers is not done atomically then there must be a read
>> barrier in weak array at: so that reading from an old space weak array that
>> is holding stale un-nilled references to unmarked objects. Tricks such as
>> including the weak bit in bounds calculations can make this cheap for
>> non-weak arrays. Alternatively nilling weak pointers can be made an atomic
>> part of incremental collection, which can be made cheaper by maintaining
>> the set of weak arrays (e.g. on a list).
>> >>
>> >> The incremental collector implies a more complex write barrier.
>> Objects are of three colours, black, having been scanned, grey, being
>> scanned, and white, unreached. A mark stack holds the grey objects. If
>> the incremental collector is marking and an unmarked white object is stored
>> into a black object then the stored object must become grey, being added to
>> the mark stack. So the wrte barrier is essentially
>> >> target isYoung ifFalse:
>> >> [newValue isYoung
>> >> ifTrue: [target isInRememberedSet ifFalse:
>> >> [target addToRememberedSet]]
>> "target now refers to a young object; it is a root for scavenges"
>> >> ifFalse:
>> >> [(target isBlack
>> >> and: [igc marking
>> >> and: [newValue isWhite]]) ifTrue:
>> >> [newValue beGrey]]] "add newValue
>> to IGC's markStack for subsequent scanning"
>> >>
>> >> The incremental collector does not detect already marked objects all
>> of whose references have been overwritten by other stores (e.g. in the
>> above if newValue overwrites the sole remaining reference to a marked
>> object). So the incremental collector only guarantees to collect all
>> garbage created in cycle N at the end of cycle N + 1. The cost is hence
>> slightly worse memory density but the benefit, provided the IGC works hard
>> enough, is the elimination of long pauses due to full garbage collections,
>> which become actions of last resort or programmer desire.
>> >>
>> >> Lazy become.
>> >>
>> >> As described earlier the basic idea behind lazy become is to use
>> corpses (forwarding objects) that are followed lazily during GC and inline
>> cache miss. However, a lazy scheme cannot be used on objects with named
>> inst vars without adding checking to all inst var accesses, which we judge
>> too expensive. Instead, when becomming objects with named inst vars, we
>> scan all activations in the stack zone, eagerly becomming these references,
>> and we check for corpses when faulting in a context into the stack zone.
>> Essentially, the invariant is that there are no references to corpses from
>> the receiver slots of stack activations. A detail is whether we allow or
>> forbid pinning of closure indirection vectors, or scan the entire stack of
>> each activation. Using a special class index pun for indirection vectors
>> is a cheap way of preventing their becomming/pinning etc. Although "don't
>> do that" (don't attempt to pin/become indirection vectors) is also an
>> acceptable response.
>> >>
>> >> 60-bit immediate Floats
>> >> Representation for immediate doubles, only used in the 64-bit
>> implementation. Immediate doubles have the same 52 bit mantissa as IEEE
>> double-precision floating-point, but only have 8 bits of exponent. So
>> they occupy just less than the middle 1/8th of the double range. They
>> overlap the normal single-precision floats which also have 8 bit exponents,
>> but exclude the single-precision denormals (exponent-127) and the
>> single-precsion NaNs (exponent +127). +/- zero is just a pair of values
>> with both exponent and mantissa 0.
>> >> So the non-zero immediate doubles range from
>> >> +/- 0x3800,0000,0000,0001 / 5.8774717541114d-39
>> >> to +/- 0x47ff,ffff,ffff,ffff / 6.8056473384188d+38
>> >> The encoded tagged form has the sign bit moved to the least
>> significant bit, which allows for faster encode/decode because offsetting
>> the exponent can't overflow into the sign bit and because testing for +/- 0
>> is an unsigned compare for <= 0xf:
>> >> msb
>> lsb
>> >> [8 exponent subset bits][52 mantissa bits ][1 sign bit][3 tag bits]
>> >> So assuming the tag is 5, the tagged non-zero bit patterns are
>> >> 0x0000,0000,0000,001[d/5]
>> >> to 0xffff,ffff,ffff,fff[d/5]
>> >> and +/- 0d is 0x0000,0000,0000,000[d/5]
>> >> Encode/decode of non-zero values in machine code looks like:
>> >> msb
>> lsb
>> >> Decode:
>> [8expsubset][52mantissa][1s][3tags]
>> >> shift away tags: [ 000
>> ][8expsubset][52mantissa][1s]
>> >> add exponent offset: [ 11 exponent ][52mantissa][1s]
>> >> rot sign: [1s][ 11 exponent
>> ][52mantissa]
>> >>
>> >> Encode: [1s][ 11 exponent
>> ][52mantissa]
>> >> rot sign: [ 11 exponent
>> ][52mantissa][1s]
>> >> sub exponent offset: [ 000 ][8expsubset][52 mantissa][1s]
>> >> shift: [8expsubset][52
>> mantissa][1s][ 000 ]
>> >> or/add tags: [8expsubset][52mantissa][1s][3tags]
>> >> but is slower in C because
>> >> a) there is no rotate, and
>> >> b) raw conversion between double and quadword must (at least in the
>> source) move bits through memory ( quadword = *(q64 *)&doubleVariable).
>> >>
>> >> Issues:
>> >> How do we avoid the Size4Bit for 64-bits? The format word encodes the
>> number of odd bytes, but currently has only 4 bits and hence only supports
>> odd bytes of 0 - 3. We need odd bytes of 0 - 7. But I don't like the
>> separate Size4Bit. Best to change the VI code and have a 5 bit format? We
>> loose one bit but save two bits (isEphemeron and isWeak (or three, if
>> isPointers)) for a net gain of one (or two)
>> >>
>> >> Further, keep Squeak's format idea or go for separate bits? For
>> 64-bits we need a 5 bit format field. This contrasts with isPointers,
>> isWeak, isEphemeron, 3 odd size bits (or byte size).. format field is
>> quite economical.
>> >>
>> >> Are class indices in inline caches strong references to classes or
>> weak references?
>> >> If strong then they must be scanned during GC and the methodZone must
>> be flushed on fullGC to reclaim all classes (this looks to be a bug in the
>> V3 Cogit).
>> >> If weak then when the class table loses references, PICs containing
>> freed classes must be freed and then sends to freed PICs or containing
>> freed clases must be unlinked.
>> >> The second approach is faster; the common case is scanning the class
>> table, the uncommon case is freeing classes. The second approach is
>> better; in-line caches do not prevent reclamation of classes.
>> >>
>> >>
>> >> Stef
>> >>
>> >>
>> >>
>> >> --
>> >> best,
>> >> Eliot
>> >>
>> >
>>
>>
>>
>
>
> --
> best,
> Eliot
>
>
>
--
Mariano
http://marianopeck.wordpress.com
May 30, 2012
Re: [Pharo-project] Zinc/Zodiac Documentation
by Sven Van Caekenberghe
Esteban,
On 30 May 2012, at 20:25, Esteban A. Maringolo wrote:
> Sven,
>
> First of all congratulation for your work. It is really good.
Thx.
> Having ported Swazoo to Dolphin I can figure what it takes to create a whole
> new HTTP server/client, so again, kudos to you.
>
> Regarding the docs, I would switch the links, leaving the no-js option as
> the default. That will avoid having to cancel the request and reading there
> is an issue with Chrome. Here it doesn't work neither in Chrome nor Chrome
> Canary (Win 7 Pro).
>
> I look forward to start developing in Pharo again for personal pet-projects,
> meanwhile I still earn my wage by working with Dolphin, and lately building
> HTTP API's on it with Swazoo.
>
> One of the things that got my attention was how/why you implemented the
> ServerDelegates. I would really appreciatie if you want to share with me/us
> what was the rationale behind them in comparison with the "alternative"
> SwazooResources. Pros and cons, and so on.
>
> I found the SwazooResources pretty neat to work with, even to implement
> special lookups by double dispatching through the URIResolution class.
>
> Best regards,
>
> --
> Esteban
I did not really think a lot about it: I choose the simplest concept that could work.
I guess, from the name and a quick google, that SwazooResource also implies dispatching/routing ?
I wanted to avoid that, hence the choice for the delegate concept.
I think that something along the lines of SwazooResources could be implemented as a delegate, an extension to Zn.
Regards,
Sven
--
Sven Van Caekenberghe
http://stfx.eu
Smalltalk is the Red Pill
May 30, 2012
Re: [Pharo-project] Plan/discussion/communication around new object format
by Eliot Miranda
On Wed, May 30, 2012 at 12:59 PM, Stéphane Ducasse <
stephane.ducasse(a)inria.fr> wrote:
> I would like to be sure that we can have
> - bit for immutable objects
> - bits for experimenting.
>
There will be quite a few. And one will be able to steal bits from the
class field if one needs fewer classes. I'm not absolutely sure of the
layout yet. But for example
8: slot size (255 => extra header word with large size)
3: odd bytes/fixed fields (odd bytes for non-pointer, fixed fields for
pointer, 7 => # fixed fields is in the class)
4 bits: format (pointers, indexable, bytes/shorts/longs/doubles indexable,
compiled method, ephemerons, weak, etc)
1: immutability
3: GC 2 mark bits. 1 forwarded bit
20: identity hash
20: class index
still leaves 5 bits unused. And stealing 4 bits each from class index
still leaves 64k classes. So this format is simple and provides lots of
unused bits. The format field is a great idea as it combines a number of
orthogonal properties in very few bits. I don't want to include odd bytes
in format because I think a separate field that holds odd bytes and fixed
fields is better use of space. But we can gather statistics before
deciding.
> Stef
>
> On May 30, 2012, at 8:48 AM, Stéphane Ducasse wrote:
>
> > Hi guys
> >
> > Here is an important topic I would like to see discussed so that we see
> how we can improve and join forces.
> > May a mail discussion then a wiki for the summary would be good.
> >
> >
> > stef
> >
> >
> >
> > Begin forwarded message:
> >
> >> From: Eliot Miranda <eliot.miranda(a)gmail.com>
> >> Subject: Re: Plan/discussion/communication around new object format
> >> Date: May 27, 2012 10:49:54 PM GMT+02:00
> >> To: Stéphane Ducasse <stephane.ducasse(a)inria.fr>
> >>
> >>
> >>
> >> On Sat, May 26, 2012 at 1:46 AM, Stéphane Ducasse <
> stephane.ducasse(a)inria.fr> wrote:
> >> Hi eliot
> >>
> >> do you have a description of the new object format you want to
> introduce?
> >>
> >> The design is in the class comment of CogMemoryManager in the Cog
> VMMaker packages.
> >>
> >> Then what is your schedule?
> >>
> >> This is difficult. I have made a small start and should be able to
> spend time on it starting soon. I want to have it finished by early next
> year. But it depends on work schedules etc.
> >>
> >>
> >> I would like to see if we can allocate igor/esteban time before we run
> out of money
> >> to help on that important topic.
> >> Now the solution is unclear and I did not see any document where we can
> evaluate
> >> and plan how we can help. So do you want help on that topic? Then how
> can people
> >> contribute if any?
> >>
> >> The first thing to do is to read the design document, to see if the
> Pharo community thinks it is the right direction, and to review it, spot
> deficiencies etc. So please get those interested to read the class comment
> of CogMemoryManager in the latest VMMaker.oscog.
> >>
> >> Here's the current version of it:
> >>
> >> CogMemoryManager is currently a place-holder for the design of the new
> Cog VM's object representation and garbage collector. The goals for the GC
> are
> >>
> >> - efficient object representation a la Eliot Miranda's VisualWorks
> 64-bit object representation that uses a 64-bit header, eliminating direct
> class references so that all objects refer to their classes indirectly.
> Instead the header contains a constant class index, in a field smaller
> than a full pointer, These class indices are used in inline and first-level
> method caches, hence they do not have to be updated on GC (although they do
> have to be traced to be able to GC classes). Classes are held in a sparse
> weak table. The class table needs only to be indexed by an instance's
> class index in class hierarchy search, in the class primitive, and in
> tracing live objects in the heap. The additional header space is allocated
> to a much expanded identity hash field, reducing hash efficiency problems
> in identity collections due to the extremely small (11 bit) hash field in
> the old Squeak GC. The identity hash field is also a key element of the
> class index scheme. A class's identity hash is its index into the class
> table, so to create an instance of a class one merely copies its identity
> hash into the class index field of the new instance. This implies that
> when classes gain their identity hash they are entered into the class table
> and their identity hash is that of a previously unused index in the table.
> It also implies that there is a maximum number of classes in the table.
> At least for a few years 64k classes should be enough. A class is entered
> into the class table in the following operations:
> >> behaviorHash
> >> adoptInstance
> >> instantiate
> >> become (i.e. if an old class becomes a new class)
> >> if target class field's = to original's id hash
> >> and replacement's id hash is zero
> >> enter replacement in class table
> >> behaviorHash is a special version of identityHash that must be
> implemented in the image by any object that can function as a class (i.e.
> Behavior).
> >>
> >> - more immediate classes. An immediate Character class would speed up
> String accessing, especially for WideString, since no instatiation needs to
> be done on at:put: and no dereference need be done on at:. In a 32-bit
> system tag checking is complex since it is thought important to retain
> 31-bit SmallIntegers. Hence, as in current Squeak, the least significant
> bit set implies a SmallInteger, but Characters would likely have a tag
> pattern of xxx10. Hence masking with 11 results in two values for
> SmallInteger, xxx01 and xxx11. 30-bit characters are more than adequate
> for Unicode. In a 64-bit system we can use the full three bits and
> usefully implement an immediate Float. As in VisualWorks a functional
> representation takes three bits away from the exponent. Rotating to put
> the sign bit in the least significant non-tag bit makes expanding and
> contracting the 8-bit exponent to the 11-bit IEEE double exponent easy ad
> makes comparing negative and positive zero easier (an immediate Float is
> zero if its unsigned 64-bits are < 16). So the representation looks like
> >> | 8 bit exponent | 52 bit mantissa | sign bit | 3 tag bits |
> >> For details see "60-bit immediate Floats" below.
> >>
> >>
> >> - efficient scavenging. The current Squeak GC uses a slow
> pointer-reversal collector that writes every field in live objects three
> times in each collection, twice in the pointer-reversing heap traversal to
> mark live objects and once to update the pointer to its new location. A
> scavenger writes every field of live data twice in each collection, once as
> it does a block copy of the object when copying to to space, once as it
> traverses the live pointers in the to space objects. Of course the block
> copy is a relatively cheap write.
> >>
> >> - lazy become. The JIT's use of inline cacheing provides a cheap way
> of avoiding scanning the heap as part of a become (which is the simple
> approach to implementing become in a system with direct pointers). A
> becomeForward: on a (set of) non-zero-sized object(s) turns the object into
> a "corpse" or "forwarding object" whose first (non-header) word/slot is
> replaced by a pointer to the target of the becomeForward:. The corpse's
> class index is set to one that identifies corpses and, because it is a
> hidden class index, will always fail an inline cache test. The inline
> cache failure code is then responsible for following the forwarding pointer
> chain (these are Iliffe vectors :) ) and resolving to the actual target.
> We have yet to determine exactly how this is done (e.g. change the
> receiver register and/or stack contents and retry the send, perhaps
> scanning the current activation). See below on how we deal with becomes on
> objects with named inst vars. Note that we probably don't have to worry
> about zero-sized objects. These are unlikely to be passed through the FFI
> (there is nothing to pass :) ) and so will rarely be becommed. If they do,
> they can become slowly. Alternatively we can insist that objects are at
> least 16 bytes in size (see a8-byte alignment below) so that there will
> always be space for a forwarding pointer. Since none of the immediate
> classes can have non-immediate instances and since we allocate the
> immediate classes indices corresponding to their tag pattern (SmallInteger
> = 1, Character = 3, SmallFloat = 4?) we can use all the class indices from
> 0 to 7 for special uses, 0 = forward, and e.g. 1 = header-sized filler.
> >>
> >> - pinning. To support a robust and easy-to-use FFI the memory manager
> must support temporary pinning where individual objects can be prevented
> from being moved by the GC for as long as required, either by being one of
> an in-progress FFI call's arguments, or by having pinning asserted by a
> primitive (allowing objects to be passed to external code that retains a
> reference to the object after returning). Pinning probably implies a
> per-object "is-pinned" bit in the object header. Pinning will be done via
> lazy become; i..e an object in new space will be becommed into a pinned
> object in old space. We will only support pinning in old space.
> >>
> >> - efficient old space collection. An incremental collector (a la
> Dijkstra's three colour algorithm) collects old space, e.g. via an amount
> of tracing being hung off scavenges and/or old space allocations at an
> adaptive rate that keeps full garbage collections to a minimum. (see free
> space/free list below)
> >>
> >> - 8-byte alignment. It is advantageous for the FFI, for floating-point
> access, for object movement and for 32/64-bit compatibility to keep object
> sizes in units of 8 bytes. For the FFI, 8-byte alignment means passing
> objects to code that expects that requirement (such as modern x86 numeric
> processing instructions). This implies that
> >> - the starts of all spaces are aligned on 8-byte boundaries
> >> - object allocation rounds up the requested size to a multiple of
> 8 bytes
> >> - the overflow size field is also 8 bytes
> >> We shall probably keep the minimum object size at 16 bytes so that
> there is always room for a forwarding pointer. But this implies that we
> will need to implement an 8-byte filler to fill holes between objects > 16
> bytes whose length mod 16 bytes is 8 bytes and following pinned objects.
> We can do this using a special class index, e.g. 1, so that the method
> that answers the size of an object looks like, e.g.
> >> chunkSizeOf: oop
> >> <var: #oop type: #'object *'>
> >> ^object classIndex = 1
> >> ifTrue: [BaseHeaderSize]
> >> ifFalse: [BaseHeaderSize
> >> + (object slotSize = OverflowSlotSize
> >> ifTrue: [OverflowSizeBytes]
> >> ifFalse: [0])
> >> + (object slotSize * BytesPerSlot)]
> >>
> >> chunkStartOf: oop
> >> <var: #oop type: #'object *'>
> >> ^(self cCoerceSimple: oop to: #'char *')
> >> - ((object classIndex = 1
> >> or: [object slotSize ~= OverflowSlotSize])
> >> ifTrue: [0]
> >> ifFalse: [OverflowSizeBytes])
> >>
> >> For the moment we do not tackle the issue of heap growth and shrinkage
> with the ability to allocate and deallocate heap segments via
> memory-mapping. This technique allows space to be released back to the OS
> by unmapping empty segments. We may revisit this but it is not a key
> requirement for the first implementation.
> >>
> >> The basic approach is to use a fixed size new space and a growable old
> space. The new space is a classic three-space nursery a la Ungar's
> Generation Scavenging, a large eden for new objects and two smaller
> survivor spaces that exchange roles on each collection, one being the to
> space to which surviving objects are copied, the other being the from space
> of the survivors of the previous collection, i.e. the previous to space.
> >>
> >> To provide apparent pinning in new space we rely on lazy become. Since
> most pinned objects will be byte data and these do not require stack zone
> activation scanning, the overhead is simply an old space allocation and
> corpsing.
> >>
> >> To provide pinning in old space, large objects are implicitly pinned
> (because it is expensive to move large objects and, because they are both
> large and relatively rare, they contribute little to overall fragmentation
> - as in aggregates, small objects can be used to fill-in the spaces between
> karge objects). Hence, objects above a particular size are automatically
> allocated in old space, rather than new space. Small objects are pinned as
> per objects in new space, by asserting the pin bit, which will be set
> automaticaly when allocating a large object. As a last resort, or by
> programmer control (the fullGC primitive) old space is collected via
> mark-sweep (mark-compact) and so the mark phase must build the list of
> pinned objects around which the sweep/compact phase must carefully step.
> >>
> >> Free space in old space is organized by a free list/free tree as in
> Eliot's VisualWorks 5i old space allocator. There are 64 free lists,
> indices 1 through 63 holding blocks of space of that size, index 0 holding
> a semi-balanced ordered tree of free blocks, each node being the head of
> the list of free blocks of that size. At the start of the mark phase the
> free list is thrown away and the sweep phase coallesces free space and
> steps over pinned objects as it proceeds. We can reuse the forwarding
> pointer compaction scheme used in the old collector. Incremental
> collections merely move unmarked objects to the free lists (as well as
> nilling weak pointers in weak arrays and scheduling them for finalization).
> The occupancy of the free lists is represented by a bitmap in a 64-bit
> integer so that an allocation of size 63 or less can know whether there
> exists a free chunk of that size, but more importantly can know whether a
> free chunk larger than it exists in the fixed size free lists without
> having to search all larger free list heads.
> >>
> >> The incremental collector (a la Dijkstra's three colour algorithm)
> collects old space via an amount of tracing being hung off scavenges and/or
> old space allocations at an adaptive rate that keeps full garbage
> collections to a minimum. [N.B. Not sure how to do this yet. The
> incremental collector needs to complete a pass often enough to reclaim
> objects, but infrequent enough not to waste time. So some form of feedback
> should work. In VisualWorks tracing is broken into quanta or work where
> image-level code determines the size of a quantum based on how fast the
> machine is, and how big the heap is. This code could easily live in the
> VM, controllable through vmParameterAt:put:. An alternative would be to
> use the heartbeat to bound quanta by time. But in any case some amount of
> incremental collection would be done on old space allocation and
> scavenging, the ammount being chosen to keep pause times acceptably short,
> and at a rate to reclaim old space before a full GC is required, i.e. at a
> rate proportional to the growth in old space]. The incemental collector is
> a state machine, being either marking, nilling weak pointers, or freeing.
> If nilling weak pointers is not done atomically then there must be a read
> barrier in weak array at: so that reading from an old space weak array that
> is holding stale un-nilled references to unmarked objects. Tricks such as
> including the weak bit in bounds calculations can make this cheap for
> non-weak arrays. Alternatively nilling weak pointers can be made an atomic
> part of incremental collection, which can be made cheaper by maintaining
> the set of weak arrays (e.g. on a list).
> >>
> >> The incremental collector implies a more complex write barrier.
> Objects are of three colours, black, having been scanned, grey, being
> scanned, and white, unreached. A mark stack holds the grey objects. If
> the incremental collector is marking and an unmarked white object is stored
> into a black object then the stored object must become grey, being added to
> the mark stack. So the wrte barrier is essentially
> >> target isYoung ifFalse:
> >> [newValue isYoung
> >> ifTrue: [target isInRememberedSet ifFalse:
> >> [target addToRememberedSet]]
> "target now refers to a young object; it is a root for scavenges"
> >> ifFalse:
> >> [(target isBlack
> >> and: [igc marking
> >> and: [newValue isWhite]]) ifTrue:
> >> [newValue beGrey]]] "add newValue
> to IGC's markStack for subsequent scanning"
> >>
> >> The incremental collector does not detect already marked objects all of
> whose references have been overwritten by other stores (e.g. in the above
> if newValue overwrites the sole remaining reference to a marked object).
> So the incremental collector only guarantees to collect all garbage
> created in cycle N at the end of cycle N + 1. The cost is hence slightly
> worse memory density but the benefit, provided the IGC works hard enough,
> is the elimination of long pauses due to full garbage collections, which
> become actions of last resort or programmer desire.
> >>
> >> Lazy become.
> >>
> >> As described earlier the basic idea behind lazy become is to use
> corpses (forwarding objects) that are followed lazily during GC and inline
> cache miss. However, a lazy scheme cannot be used on objects with named
> inst vars without adding checking to all inst var accesses, which we judge
> too expensive. Instead, when becomming objects with named inst vars, we
> scan all activations in the stack zone, eagerly becomming these references,
> and we check for corpses when faulting in a context into the stack zone.
> Essentially, the invariant is that there are no references to corpses from
> the receiver slots of stack activations. A detail is whether we allow or
> forbid pinning of closure indirection vectors, or scan the entire stack of
> each activation. Using a special class index pun for indirection vectors
> is a cheap way of preventing their becomming/pinning etc. Although "don't
> do that" (don't attempt to pin/become indirection vectors) is also an
> acceptable response.
> >>
> >> 60-bit immediate Floats
> >> Representation for immediate doubles, only used in the 64-bit
> implementation. Immediate doubles have the same 52 bit mantissa as IEEE
> double-precision floating-point, but only have 8 bits of exponent. So
> they occupy just less than the middle 1/8th of the double range. They
> overlap the normal single-precision floats which also have 8 bit exponents,
> but exclude the single-precision denormals (exponent-127) and the
> single-precsion NaNs (exponent +127). +/- zero is just a pair of values
> with both exponent and mantissa 0.
> >> So the non-zero immediate doubles range from
> >> +/- 0x3800,0000,0000,0001 / 5.8774717541114d-39
> >> to +/- 0x47ff,ffff,ffff,ffff / 6.8056473384188d+38
> >> The encoded tagged form has the sign bit moved to the least significant
> bit, which allows for faster encode/decode because offsetting the exponent
> can't overflow into the sign bit and because testing for +/- 0 is an
> unsigned compare for <= 0xf:
> >> msb
> lsb
> >> [8 exponent subset bits][52 mantissa bits ][1 sign bit][3 tag bits]
> >> So assuming the tag is 5, the tagged non-zero bit patterns are
> >> 0x0000,0000,0000,001[d/5]
> >> to 0xffff,ffff,ffff,fff[d/5]
> >> and +/- 0d is 0x0000,0000,0000,000[d/5]
> >> Encode/decode of non-zero values in machine code looks like:
> >> msb
> lsb
> >> Decode: [8expsubset][52mantissa][1s][3tags]
> >> shift away tags: [ 000 ][8expsubset][52mantissa][1s]
> >> add exponent offset: [ 11 exponent ][52mantissa][1s]
> >> rot sign: [1s][ 11 exponent
> ][52mantissa]
> >>
> >> Encode: [1s][ 11 exponent
> ][52mantissa]
> >> rot sign: [ 11 exponent
> ][52mantissa][1s]
> >> sub exponent offset: [ 000 ][8expsubset][52 mantissa][1s]
> >> shift: [8expsubset][52
> mantissa][1s][ 000 ]
> >> or/add tags: [8expsubset][52mantissa][1s][3tags]
> >> but is slower in C because
> >> a) there is no rotate, and
> >> b) raw conversion between double and quadword must (at least in the
> source) move bits through memory ( quadword = *(q64 *)&doubleVariable).
> >>
> >> Issues:
> >> How do we avoid the Size4Bit for 64-bits? The format word encodes the
> number of odd bytes, but currently has only 4 bits and hence only supports
> odd bytes of 0 - 3. We need odd bytes of 0 - 7. But I don't like the
> separate Size4Bit. Best to change the VI code and have a 5 bit format? We
> loose one bit but save two bits (isEphemeron and isWeak (or three, if
> isPointers)) for a net gain of one (or two)
> >>
> >> Further, keep Squeak's format idea or go for separate bits? For
> 64-bits we need a 5 bit format field. This contrasts with isPointers,
> isWeak, isEphemeron, 3 odd size bits (or byte size).. format field is
> quite economical.
> >>
> >> Are class indices in inline caches strong references to classes or weak
> references?
> >> If strong then they must be scanned during GC and the methodZone must
> be flushed on fullGC to reclaim all classes (this looks to be a bug in the
> V3 Cogit).
> >> If weak then when the class table loses references, PICs containing
> freed classes must be freed and then sends to freed PICs or containing
> freed clases must be unlinked.
> >> The second approach is faster; the common case is scanning the class
> table, the uncommon case is freeing classes. The second approach is
> better; in-line caches do not prevent reclamation of classes.
> >>
> >>
> >> Stef
> >>
> >>
> >>
> >> --
> >> best,
> >> Eliot
> >>
> >
>
>
>
--
best,
Eliot
May 30, 2012
Re: [Pharo-project] Zinc/Zodiac Documentation
by Sven Van Caekenberghe
Sean,
On 30 May 2012, at 19:06, Sean P. DeNigris wrote:
> Sven Van Caekenberghe wrote
>> Now that I have recovered from the intensive Pharo Conference, I found the
>> time to publish the Zinc and Zodiac documentation.
>> ...
>> http://zn.stfx.eu/zn/zinc-http-components-paper.html
>> http://zdc.stfx.eu/zodiac-paper.html
>
> Wow, really clear and thorough. The way the raw internet protocols are woven
> into the examples is really cool (I always have to be reminded that there's
> no magic, just binary turtles all the way down)⦠And I found
> numberOfRetries:, retryDelay:, contentReader:, and #enforceHttpSuccess:
> particularly useful in simplifying my code
I am glad it is useful to you.
> A few thoughts:
> * ZnClient>>retryDelay:
> - it'd be nice if it took aDuration. If I was reading someone else's code,
> "client retryDelay: 30" forces me to find the method, but "client
> retryDelay: 30 seconds" is clear
Yeah, I thought about that too, and I guess at one point I decided to go for seconds as an integer. Note that apart from #retryDelay:, there are also #timeout: and #connectionReuseTimeout:. I think I did that because SocketStream uses seconds as timeout value too. Now I will think about changing that, it feels nicer with Durations.
> - I think it should return self, like enforceHttpSuccess:. Besides
> consistency, it will avoid having to send #yourself when configuring a new
> client instance.
You are right (copy/paste coding from the getters).
==================== Summary ====================
Name: Zinc-HTTP-SvenVanCaekenberghe.280
Author: SvenVanCaekenberghe
Time: 30 May 2012, 10:14:53 pm
UUID: 00d1da5e-18a2-4f96-afe7-c7f7d6fe0c6c
Ancestors: Zinc-HTTP-SvenVanCaekenberghe.279
clean up ZnClient option setter methods to return self for easy chaining (thx Sean DeNigris)
> * JSJsonParser - where does this class live? A reference would be greatâ¦
It is in Seaside ;-)
> * FileSystem - I remember you mentioned having a problem with FS. Has this
> been solved? There is a slice in the inbox which replaces all uases of
> FileDirectory and friends with FS. Also, how will this effect portability
> (e.g. to Squeak). If you make Zn depend on FS, is the Pharo (renamed
> classes) version of FS loadable in Squeak trunk?
Yes, that will be a challenge ;-)
I guess it won't work without a simple compatibility layer.
> * [up/down]load convenience methods - What do you think about adding e.g.
> ZnEasy>>download:to: & upload:to: (is POST a good default?)
Hmm. the goal is not to push ZnEasy too much. I'll consider it.
Thanks for the feedback,
Sven
--
Sven Van Caekenberghe
http://stfx.eu
Smalltalk is the Red Pill
May 30, 2012