<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Apr 8, 2016 at 9:29 AM, Levente Uzonyi <span dir="ltr"><<a href="mailto:leves@caesar.elte.hu" target="_blank">leves@caesar.elte.hu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"> <br>On Fri, 8 Apr 2016, Bert Freudenberg wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<br>
On 08.04.2016, at 00:34, Levente Uzonyi <<a href="mailto:leves@caesar.elte.hu" target="_blank">leves@caesar.elte.hu</a>> wrote:<br>
><br>
> When the VM runs out of memory primitive 71 in #basicNew: will fail and it'll send<br>
> #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<br>
object.<br>
<br>
I couldn’t believe regular GC would go through primitive failure code and rely on the image to invoke a full GC.<br>
</blockquote>
<br>
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.<br>
<br>
I had replaced<br>
<br>
Smalltalk garbageCollect < bytesRequested ifTrue:<br>
[Smalltalk growMemoryByAtLeast: bytesRequested].<br>
<br>
with<br>
<br>
Smalltalk garbageCollectMost < bytesRequested ifTrue:<br>
[Smalltalk garbageCollect < bytesRequested ifTrue:<br>
[Smalltalk growMemoryByAtLeast: bytesRequested]].<br>
<br>
in #handleFailingBasicNew: and the number of full GCs has never gone above 4 when running this snippet:<br>
<br>
Smalltalk garbageCollect.<br>
x := Smalltalk vmParameterAt: 7. "num full GC since startup"<br>
{ [ActiveWorld imageForm] bench.<br>
(Smalltalk vmParameterAt: 7) - x }<br>
<br>
Doing the same trick in #handleFailingBasicNew has taken the number down to 2.<br>
So I suggest we should use both changes if it has no negative side effects. Eliot? :)<br></blockquote><div><br></div><div>+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 <span style="color:rgb(0,0,0);font-size:12.8px">#handleFailingBasicNew: runs the scavenger will run, and so there is no need to do </span>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.</div><div><br></div><div>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 <span style="color:rgb(0,0,0);font-size:12.8px">#handleFailingBasicNew: et al calls </span>Smalltalk garbageCollectMost explicitly? </div><div><br></div><div>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):</div><div><br></div><div><div>maxSlotsForNewSpaceAlloc</div><div><span class="" style="white-space:pre">        </span>"Almost entirely arbitrary, but we dont want 1Mb bitmaps allocated in eden.</div><div><span class="" style="white-space:pre">        </span> But this choice means no check for numSlots > maxSlotsForNewSpaceAlloc</div><div><span class="" style="white-space:pre">        </span> for non-variable allocations."</div><div><span class="" style="white-space:pre">        </span>^self fixedFieldsOfClassFormatMask</div><div><br></div><div>fixedFieldsOfClassFormatMask</div><div><span class="" style="white-space:pre">        </span><api></div><div><span class="" style="white-space:pre">        </span>^1 << self fixedFieldsFieldWidth - 1</div><div><span class="" style="white-space:pre">        </span></div><div>fixedFieldsFieldWidth</div><div><span class="" style="white-space:pre">        </span><api></div><div><span class="" style="white-space:pre">        </span><cmacro></div><div><span class="" style="white-space:pre">        </span>^16</div></div><div><br></div><div>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 <span style="color:rgb(0,0,0);font-size:12.8px">handleFailingBasicNew: call </span>garbageCollectMost explicitly.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
And in fact, it doesn’t. I put a "BasicNewFailures := BasicNewFailures + <br>
</blockquote>
1.” in basicNew(:) and it never increased. So I think this is invoked<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
only in severe conditions.<br>
</blockquote>
<br>
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.<br></blockquote><div><br></div><div><br class="">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 :-)<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
> Only when it's not possible to make room for the new object by collecting the garbage will the VM allocate more memory.<br>
<br>
I see that code in Behavior (calling growMemoryByAtLeast:), yes, but in my tests it never got invoked.<br>
<br>
> 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,<br>
because the code became way too snappy and it quickly reached the new memory limit.<br>
<br>
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<br>
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.<br>
<br>
</blockquote>
<br>
If you replace #bench with #benchFor: 1 seconds, then the number of full GCs will stay zero (using the #garbageCollectMost change I suggested above).<br>
<br>
Levente<br>
<br></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature"><div dir="ltr"><div><span style="font-size:small;border-collapse:separate"><div>_,,,^..^,,,_<br></div><div>best, Eliot</div></span></div></div></div>
</div></div>