<div dir="ltr">HiChris, Hi All,<div><br></div><div>   Chris, forgive me for replying to vm-dev, but I want to canvas responses to the solutions I&#39;m proposing, (and I want to spread this good news) so I&#39;m begging forgiveness instead of asking permission ;-)<br><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Nov 6, 2014 at 7:31 PM, Chris Muller <span dir="ltr">&lt;<a href="mailto:ma.chris.m@gmail.com" target="_blank">ma.chris.m@gmail.com</a>&gt;</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">Hi Eliot.  The good news is, with a minor tweak to a timeout delay,<br>
the Magma test suite actually *finished to completion*!<br>
Yeeeeeaaaaahhh!!!<br></blockquote><div><br></div><div>Fantastic news.  Your&#39;s is a great test of the system.  Thanks for your efforts in getting us this far!</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">But its that required minor tweak that exposes the bad news, though --<br>
there seems to be some sort of performance issue when accessing via<br>
#instVarAt:.<br>
<br>
In the following script, Cog reports 18M per second where Spur reports<br>
120K per second.<br>
<br>
| arr| arr := Array new: 1000.<br>
[arr instVarAt: 1000] bench<br>
<br>
Also, observe changing the Array size to 1 and instVarAt: 1 speeds up<br>
dramatically.  This makes me think there is some sort of enumerating /<br>
looping going on in #instVarAt: primitive.<br></blockquote><div><br></div><div>Actually it is going on in the primitive failure machinery, not the primitive itself, but ok...  this performance is to be expected.  Here&#39;s the definition of instVarAt:</div><div><br></div><div><div>instVarAt: index </div><div><span class="" style="white-space:pre">        </span>&quot;Primitive. Answer a fixed variable in an object. The numbering of the </div><div><span class="" style="white-space:pre">        </span>variables corresponds to the named instance variables. Fail if the index </div><div><span class="" style="white-space:pre">        </span>is not an Integer or is not the index of a fixed variable. Essential. See </div><div><span class="" style="white-space:pre">        </span>Object documentation whatIsAPrimitive.&quot;</div><div><br></div><div><span class="" style="white-space:pre">        </span>&lt;primitive: 73&gt;</div><div><span class="" style="white-space:pre">        </span>&quot;Access beyond fixed variables.&quot;</div><div><span class="" style="white-space:pre">        </span>^self basicAt: index - self class instSize</div></div><div><br></div><div><span style="white-space:pre">You see that the primitive is defined to access /only/ named inst vars, and to fail if access is attempted beyond the named inst vars.  Now Array has no named inst vars, and so the primtiive will always fail.  Now Spur provides transparent forwarding (and hence fast become with direct pointers) by checking for forwarding on message send (see your second issue) /and/ on primitive failure.  So whenever the instVarAt: primitive fails the VM scans the receiver and arguments to the primitive&#39;s &quot;accessor depth&quot; (in this case 1, 0 comprising stack references to the arguments, 1 </span><span style="white-space:pre">comprising</span><span style="white-space:pre"> inst vars of the receiver and arguments), looking for forwarding pointers, and if it finds any, eliminating them by following them and updating the reference, and then retrying the primitive.  If none are found, or the primitive fails a second time, execution proceeds, failing the primitive and activating its method.</span></div><div><span style="white-space:pre"><br></span></div><div>The accessor depth for a primitive is computed by analysing the primitive&#39;s parse tree (actually the closure of the primitive&#39;s parse tree over any calls it makes).  In this case the analysis concludes the depth is 1, whereas a depth of 0 would be ok in cases where there are no forwarded references in the Array; if the primitive were to answer a forwarded object, the forwarding pointer would be followed when it was sent a message.  Lots of accesses to the same slot in the ARray. if it yielded a forwarding object, would push forwarding costs to where that result was used.  So there&#39;s a toss up between eagerly following or not.<span style="white-space:pre"><br></span></div><div><br></div><div>The reason why changing the size of the array makes such a difference is that the VM has to scan the entire array and so does ~ 1000 times as much work when it has length 1000 than when it has length 1.  The assumption behind my architecture here is that primitive failure is relatively rare.  That&#39;s clearly not true in this case.   Fortunately there are a number of solutions I can think of here:</div><div><br></div><div><br></div><div>1. in Magma you can implement a new method for accessing slots, lets call it slotAt:, and it would be defined like this:</div><div><br></div><div><div>Object&gt;&gt;slotAt: index </div><div><span class="" style="white-space:pre">        </span>&quot;Access the nth slot of an object.  Use the instVarAt: primitive for speed with fixed, non-variable objects.&quot;</div><div><br></div><div><span class="" style="white-space:pre">        </span>&lt;primitive: 73&gt;</div><div><span class="" style="white-space:pre">        </span>&quot;Access beyond fixed variables.&quot;</div><div><span class="" style="white-space:pre">        </span>^self basicAt: index - self class instSize</div></div><div><br></div><div>Array&gt;&gt;slotAt: index</div><div><span class="" style="white-space:pre">        </span>&quot;Access the nth slot of an object.  Use the basicAt:: primitive for speed with variable objects.&quot;</div><div><br></div><div><span class="" style="white-space:pre">        </span>&lt;primitive: 60&gt;</div><div><span class="" style="white-space:pre">        </span>index isInteger ifTrue:</div><div><span class="" style="white-space:pre">                </span>[self class isVariable</div><div><span class="" style="white-space:pre">                        </span>ifTrue: [self errorSubscriptBounds: index]</div><div><span class="" style="white-space:pre">                        </span>ifFalse: [self errorNotIndexable]].</div><div><span class="" style="white-space:pre">        </span>index isNumber</div><div><span class="" style="white-space:pre">                </span>ifTrue: [^self slotAt: index asInteger]</div><div><span class="" style="white-space:pre">                </span>ifFalse: [self errorNonIntegerIndex]</div><div><br></div><div>This only works if Array has no subclasses with named inst vars, or at least, if there are any Magma makes no use of them.</div><div><br></div><div>2. I can add a slotAt: primitive that does not fail when access is made beyond the named inst vars, but provides useful, flat access to all the slots.  This will be very fast, but requires a little work.</div><div><br></div><div>3. re the point about what the forwarding depth should be, I could provide a &quot;manual override&quot; of the depth for specific primtiives, so that the depth for instVarAt: would be 0.  This is a little work but might have more uses beyond instVarAt:.</div><div><br></div><div>I&#39;m leaning to 2.  But want to hear waht others think, including any additional solutions.</div><div><br></div><div>Anyway, interesting.  A behaviour wonderous strange, no doubt :-)</div><div><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">(Problem #2) -- Now, here&#39;s something weird that I could not begin to<br>
guess at.  Switching to plain #at: (instead of #instVarAt:), and then<br>
compare the output with vs. without become:<br>
<br>
&quot;without become&quot;<br>
| arr| arr := Array new: 1000.<br>
[arr at: 1000] bench<br>
<br>
          &#39;56,600,000 per second.&#39;<br>
<br>
to same measurement after becomed: to the same.<br>
<br>
| arr| arr := Object new.<br>
arr becomeForward: (Array new: 1000).<br>
[arr at: 1000] bench<br>
<br>
       &#39;4,060,000 per second.&#39;<br>
<br>
So my reified Proxy&#39;s are operating much more slowly.  This<br>
degradation does not exist in Cog.<br></blockquote><div><br></div><div>So what&#39;s happening here is that when the become occurs, the Object new version of arr is changed to a forwarder to an (Array new: 1000).  When the message is sent to arr, the send fails, the send failure code scans the temp vars of the stack frame, updates arr to point directly to the (Array new: 1000) version of arr, completing the become: operation, and the send is retried.  If you change the benchmarks to</div><div><br></div><div>| arr| arr := Array new: 1000.<br>[1 to: 1000 do: [:i| arr at: i]] bench<br></div><br class="">| arr| arr := Object new.</div><div class="gmail_quote">arr becomeForward: (Array new: 1000).<br>[1 to: 1000 do: [:i| arr at: i]] bench<div><br></div><div>you should see much less difference:</div><div><br></div><div><div><br></div><div><br></div><div>| arr| arr := Array new: 1000.</div><div>[1 to: 1000 do: [:i| arr at: i]] bench &#39;163,000 per second.&#39;</div><div><br></div><div>| arr| arr := Object new.</div><div>arr becomeForward: (Array new: 1000).</div><div>[1 to: 1000 do: [:i| arr at: i]] bench &#39;161,000 per second.&#39;</div></div><div><br></div><div>So how common is it that you become an object and send a message to it only once from a reference?  Do you have any macro benchmarks?  If the macro benchmarks show substantial speedups even though micro-benchmarks show slow-downs there&#39;s no issue.  The costs in the micro operations are being paid to gain substantial advantages at the macro level.</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">PS -- Why is #instVarAt: so much slower than #at:?  Is there any way<br>
to make it just as fast?<br></blockquote><div><br></div><div>See above.</div><div> </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">PPS -- that line-ending problem when using the Clipboard that Levente<br>
mentioned the other day has dogged me all year as well.  Would it be<br>
possible to include the -textenc UTF8 by default in the squeak script<br>
that you release?<br></blockquote><div><br></div><div>Yes.  I think I can just make this the default in all VMs and/or just the Spur ones.  IIRC I found out what the name to use for the defalt was just the other day.  I&#39;m going to go find it again, but yes, I&#39;ll make this the default.  I&#39;d prefer to make it the only choice.  Is that OK with folks?</div><div><br></div><div> </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">Thank you for this (soon-to-be) awesome piece of software..<br></blockquote><div><br></div><div>sigh ;-)</div></div>-- <br><div class="gmail_signature">best,<div>Eliot</div></div>
</div></div></div>