[Vm-dev] Re: Too many full GCs in Cog/Spur ... while just drawing GUI ...

Eliot Miranda eliot.miranda at gmail.com
Fri Apr 8 18:00:24 UTC 2016


On Fri, Apr 8, 2016 at 9:29 AM, Levente Uzonyi <leves at caesar.elte.hu> wrote:

>
> On Fri, 8 Apr 2016, Bert Freudenberg wrote:
>
>>
>> On 08.04.2016, at 00:34, Levente Uzonyi <leves at caesar.elte.hu> wrote:
>> >
>> > When the VM runs out of memory primitive 71 in #basicNew: will fail and
>> it'll send
>> > #handleFailingBasicNew:. That method will retry primitive 71, which
>> will fail again, and then it'll do a full gc to make room for the new
>> object.
>>
>> I couldn’t believe regular GC would go through primitive failure code and
>> rely on the image to invoke a full GC.
>>
>
> It does. Perhaps it's worth trying #garbageCollectMost before doing a full
> GC. It decreases the full GC significantly in cases when larger objects are
> allocated and thrown away frequently.
>
> I had replaced
>
>         Smalltalk garbageCollect < bytesRequested ifTrue:
>                 [Smalltalk growMemoryByAtLeast: bytesRequested].
>
> with
>
>         Smalltalk garbageCollectMost < bytesRequested ifTrue:
>                 [Smalltalk garbageCollect < bytesRequested ifTrue:
>                         [Smalltalk growMemoryByAtLeast: bytesRequested]].
>
> in #handleFailingBasicNew: and the number of full GCs has never gone above
> 4 when running this snippet:
>
> Smalltalk garbageCollect.
> x := Smalltalk vmParameterAt: 7. "num full GC since startup"
> { [ActiveWorld imageForm] bench.
> (Smalltalk vmParameterAt: 7) - x }
>
> Doing the same trick in #handleFailingBasicNew has taken the number down
> to 2.
> So I suggest we should use both changes if it has no negative side
> effects. Eliot? :)
>

+1.  I had hoped that scavenging would be run automatically, but this won't
happen with huge allocations.  For small allocations, when eden is full,
the machine code new primitive will set the "needs scavenge" flag,
when #handleFailingBasicNew:
runs the scavenger will run, and so there is no need to do Smalltalk
garbageCollectMost, because that has happened implicitly.  But for huge
allocations I think the code doesn't set the scavenge flag, it merely fails
the primitive.  But I need to check this.

What do we prefer, having the machine code for new always set the "needs
scavenge" flag if an allocation failed because there was no room, or
have #handleFailingBasicNew:
et al calls Smalltalk garbageCollectMost explicitly?

The wrinkle here is that Spur will only allocate objects with 64k slots or
less in newSpace (I apologise; this is not documented at the image level):

maxSlotsForNewSpaceAlloc
"Almost entirely arbitrary, but we dont want 1Mb bitmaps allocated in eden.
But this choice means no check for numSlots > maxSlotsForNewSpaceAlloc
for non-variable allocations."
^self fixedFieldsOfClassFormatMask

fixedFieldsOfClassFormatMask
<api>
^1 << self fixedFieldsFieldWidth - 1
fixedFieldsFieldWidth
<api>
<cmacro>
^16

So the "needs scavenge" flag only gets set for "small" allocations, and
even more confusingly, "small" is different in 32- and 64-bits since 64k
slots is 256k bytes in 32-bits buts 512k bytes in 64-bits.  So I am leaning
on having handleFailingBasicNew: call garbageCollectMost explicitly.

And in fact, it doesn’t. I put a "BasicNewFailures := BasicNewFailures +
>>
> 1.” in basicNew(:) and it never increased. So I think this is invoked
>
>> only in severe conditions.
>>
>
> Try the snippet above. The way I found this was that I also had the #bench
> send wrapped in #timeProfile, so it appeared in the profiler. Make sure you
> have only a few windows open, otherwise drawing will take way too long and
> there won't be enough allocations to trigger the failure.
>


When I saw Bert's message above, denying that basicNew: ever fails, I
immediately repeated Bert's experiment, /knowing/ that basicNew /does/
fail.  But to my surprise my experiment revealed Bert's result, that
basicNew does not appear to fail :-).  Of course the gotcher is that the
backward branch in the loop over allocation checks for events, and runs the
scavenger, so a simple loop never shows failures.  It's difficult to make
it fail, but fail it will :-)

> Only when it's not possible to make room for the new object by collecting
>> the garbage will the VM allocate more memory.
>>
>> I see that code in Behavior (calling growMemoryByAtLeast:), yes, but in
>> my tests it never got invoked.
>>
>> > Forcing the VM to allocate more memory, by tweaking parameter 25, will
>> decrease the number of GCs, though I wasn't able to get it down to 0,
>> because the code became way too snappy and it quickly reached the new
>> memory limit.
>>
>> Yes, the number of full GCs goes down. But why not to zero? I cannot
>> understand why we cannot find a large-enough “new space size” so that the
>> working set is fully kept in there. Redrawing a couple windows should not
>> produce any long-lived objects that trigger a full GC every second.
>>
>>
> If you replace #bench with #benchFor: 1 seconds, then the number of full
> GCs will stay zero (using the #garbageCollectMost change I suggested above).
>
> Levente
>
>


-- 
_,,,^..^,,,_
best, Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20160408/6d518cf7/attachment-0001.htm


More information about the Vm-dev mailing list