help-smalltalk
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Help-smalltalk] the test test - a more detailed analysis of image c


From: Paolo Bonzini
Subject: Re: [Help-smalltalk] the test test - a more detailed analysis of image contents
Date: Mon, 20 Jul 2009 23:07:56 +0200
User-agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1b3pre) Gecko/20090513 Fedora/3.0-2.3.beta2.fc11 Lightning/1.0pre Thunderbird/3.0b2

Oops, hit send too early.

Let's see where they come from.

st> (c := b first allOwners) collect: [ :e | e class ]
(OrderedCollection Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array Array )
st> (d := c first allOwners) collect: [ :e | e class ]
(Iliad.LabelElement Array Array Array Array Array Array Array )
st> (e := d first allOwners first allOwners) collect: [ :e | e class ]
(Iliad.FormElement Array Array Array Array Array Array Array )
st> (f := e first allOwners first allOwners) collect: [ :e | e class ]
(Iliad.DivElement Array Array Array Array Array Array Array )
st> (g := f first allOwners first allOwners) collect: [ :e | e class ]
(BlockContext BlockContext BlockContext BlockContext Array Array )

st> g first method
[] in OnlineTester.OTFrageWidget>>contents
st> g second method
[] in OnlineTester.OTFrageWidget>>contents
st> g third method
[] in OnlineTester.OTFrageWidget>>contents
st> (h := g first allOwners) collect: [ :e | e class ]
(BlockClosure Array )
st> (i := h first allOwners) collect: [ :e | e class ]
(Iliad.Action Array Array )
st> (j := i first  allOwners) collect: [ :e | e class ]
(MethodContext CallinProcess MethodContext MethodContext MethodContext Association BlockClosure BlockClosure BlockClosure BlockClosure BlockClosure BlockClosure MethodContext CallinProcess MethodContext Array )

Uh-oh.  Quite a few owners, in particular:

st> (j at: 6)
'100058'->an Action
st> (k := (j at: 6) allOwners) collect: [ :e | e class ]
...
st> (k at: 8) class
Dictionary
st> (k at: 8) size
681

681 actions means that using a Dictionary for them was not bad (just a LookupTable would save memory). Sorry Nicolas for your ActionRegistry. :-)

At least, the number of widgets is what you'd expect:

st> actions := (k at: 8) asArray
st> widgets := (IdentitySet withAll: (actions collect: [:e |e block receiver]) asArray
st> widgets size
95
st> widgets first frage aufgabe test aufgaben inject: 0 into: [ :o :a | o + a fragen size ]
95

... and tells us that the dictionary covered the Actions in a single session as expected (there are 95*27 OTFrageWidgets, 95 per pupil). The number of actions overall is also correct, so there is no leak here: it should be roughly 2*number of answers per quiz (the AJAX request rebuilds the tree and creates new actions), and we get

st> widgets first frage aufgabe test aufgaben inject: 0 into: [ :o :a | o + (a fragen size * a antworten size * 2) ]
610

Probably sometimes (s)he tried different answers: :-)

st> (widgets collect: [:w| actions count: [:e | e block receiver == w]]) asBag
Bag(6:61 4:1 12:7 8:14 16:1 9:11 )

Again, this is for one pupil only. Indeed we have overall over 16000 actions (as pointed out by Stefan in his first analysis), and these had better stay in the Dictionary.

Instead for the

st> ((Iliad.Element allSubclasses collect: [:e|e allInstances size])
st>    fold: [:a :b|a+b])
64222

elements given the back-of-the-envelope analysis of OrderedCollection and Dictionary usage, we are spending at least ~32 megabytes out of 90 (probably more like 40-50).

        >> IDEAS: I would probably replace the children
        OrderedCollection with a tail+next circular linked list
        representation.  At the beginning tail := nil, if it is not nil
        the head is accessible with tail next.  Inserting at the tail
        is easy.  All can be encapsulated into #childrenDo: of course,
        and it saves a slot compared to head+tail+next.  Consider that
        the OrderedCollection also costs two slots (one slot for the OC
        instance variable and one slot in the OC itself) so you'll save
        memory for the empty OC slots (probably like 100 bytes per
        element, totalling ~6 MB).

        For the attributes dictionary, reusing the RBSmallDictionary
        code should work well, or maybe lazy initalization would be
        okay coupled with moving a few attributes out of the dictionary:

        st> ((Dictionary allInstances select: [ :e | e size = 1 ])
                collect: [ :e | e keys ]) asBag
        Bag(Set ('class' ):15498 Set (#created ):1 Set ('for' ):16613
        Set (#selectorsMap ):1 )

        st> ((Dictionary allInstances select: [ :e | e size = 2 ])
        collect: [ :e | e keys ]) asBag
        Bag(Set (#expirySeconds #charset ):1 Set (#modified #created
         ):27 Set ('class' 'id' ):5166 )

        st> ((Dictionary allInstances select: [ :e | e size = 3 ])
        collect: [ :e | e keys ]) asBag
        Bag(Set ('value' 'name' 'type' ):5166 )

        st> ((Dictionary allInstances select: [ :e | e size = 4 ])
        collect: [ :e | e keys ]) asBag
        Bag(Set ('id' #value 'name' 'type' ):11447 )

        st> ((Dictionary allInstances select: [ :e | e size = 5 ])
        collect: [ :e | e keys ]) asBag
        Bag(... Set ('accept-charset' 'class' 'method' 'action'
        'onsubmit' ):5166 )

        Again, the break-even point is very low.   Even storing
        5 attributes in all HTML elements including <br> would be
        okay, since a Dictionary costs 4 words for flags+class+
        size+object pointer, and 1 word for the tally.  But of course
        value/name/type would be stored only in an InputElement etc.,
        only id+class+lazily initialized children would be in the
        superclass.  At this point, even using a special SmallDictionary
        class might be premature optimization...

        Since forms will be common in AJAX applications, saving
        memory there is nice.  Maybe accept-charset could be left into
        attributes, but without the #ifAbsentPut: here:

    beforePrintHtml [
        <category: 'printing'>
        self attributeAt: 'action' ifAbsentPut: [self url asString].
        self attributeAt: 'method' ifAbsent: [self usePost].
        self attributeAt: 'accept-charset'
                ifAbsentPut: [self session charset].
        ...
    ]

        since 99.99% it will be sane to use the default taken from the
        session.  Probably there are more such cases where #ifAbsentPut:
        is used.

Strings are many (mostly from Action ids and from Swazoo's HTTP header parser), but they consume little memory:

st> String allInstances inject: 0 into: [ :o :i | o + (i size + 7 bitAnd: -8) + 32 ]
347616

A final note regarding Swazoo,

st> a := OrderedCollection allInstances select: [:e | e size = 3 and: [ e first isString ]]

Shows some OrderedCollection ('application/json' ' text/javascript' ' */*') indeed, but only 43.

Paolo




reply via email to

[Prev in Thread] Current Thread [Next in Thread]