<div dir="ltr">tangentally...<br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, May 7, 2018 at 1:02 PM, Chris Muller <span dir="ltr"><<a href="mailto:ma.chris.m@gmail.com" target="_blank">ma.chris.m@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)">Hi Levente,</div><span class="gmail-"><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)"> <br></div><blockquote class="gmail_quote" style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><span><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">meant.  My own criteria is "methods which are called thousands of times per minute" _today_.  <br></blockquote></span></blockquote><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)"> <br></div></span><blockquote class="gmail_quote" style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">A rule of thumb could be that if a core method (e.g. anything in Kernel/Collections) is part of an API and it's not there to support a very specific task (e.g. Dictionary >> #associationDeclareAt:), then its performance should be taken into account.<br></blockquote><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)"><br></div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)">That sounds like it only exempts utility methods ("specific tasks").  Technically, this should leave me afraid that you might want to inline Dictionary>>#at:, but I'm relatively sure you don't.  #at: is the most basic featured accessing method for understanding and using a Dictionary, so I hope you agree that's one that should retain its exemplar status.</div></div></blockquote><div><br></div><div>But Chris, Dictionary>>#at: has already been heavily optimized via scanFor:.  Here's the original Smalltalk-80 v2 code:</div><div><br></div><div><i>Dictionary methods for accessing</i></div><div><b>at:</b> key </div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>"Answer the value at key.  If key is not found, create an error message."</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>^self at: key ifAbsent: [self errorKeyNotFound]</div><div><br></div><div><b>at:</b> key <b>ifAbsent:</b> aBlock </div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>"Answer the value at key.  If key is not found, answer the</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>result of evaluating aBlock."</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span>| index |</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>index _ self findKey: key ifAbsent: [^aBlock value].</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>^(self basicAt: index) value</div><div><br></div><div><i>Dictionary methods for private</i></div><div><div><b>findKey:</b> key <b>ifAbsent:</b> aBlock </div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>| index |</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>index _ self findKeyOrNil: key.</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>(self basicAt: index) == nil ifTrue: [^aBlock value].</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>^index</div></div><div><br></div><div><div><b>findKeyOrNil:</b> key </div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span>| location length probe pass |</div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>length _ self basicSize.</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>pass _ 1.</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>location _ key hash \\ length + 1.</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">     </span>[(probe _ self basicAt: location) == nil or: [probe key = key]]</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>whileFalse: </div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                  </span>[(location _ location + 1) > length</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                         </span>ifTrue: </div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                      </span>[location _ 1.</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                 </span>pass _ pass + 1.</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                       </span>pass > 2 ifTrue: [^self grow findKeyOrNil: key]]].</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>^location</div></div><div><br></div><div>Here's the current Squeak code</div><div><br></div><div><div><b>at:</b> key </div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>"Answer the value associated with the key."</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>^ self at: key ifAbsent: [self errorKeyNotFound: key]</div></div><div><br></div><div><div><b>at:</b> key <b>ifAbsent:</b> aBlock </div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>"Answer the value associated with the key or, if key isn't found,</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">     </span>answer the result of evaluating aBlock."</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>^((array at: (self scanFor: key)) ifNil: [ aBlock ]) value "Blocks and Associations expect #value"</div></div><div><br></div><div><span style="font-style:italic">Dictionary methods for private</span><br></div><div><div><b>scanFor:</b> anObject</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span>"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or raise an error if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements."</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>| index start size |</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>index := start := anObject hash \\ (size := array size) + 1.</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>[ </div><div><span class="gmail-Apple-tab-span" style="white-space:pre">            </span>| element |</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">            </span>((element := array at: index) == nil or: [ anObject = element key ])</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                   </span>ifTrue: [ ^index ].</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">            </span>(index := index \\ size + 1) = start ] whileFalse.</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">     </span>self errorNoFreeSpace</div></div><div><br></div><div>In the v2.0 code, at: always creates a block, at:ifAbsent: always creates a block (which means two block activations on error), and the scan for elements is three activations deep.</div><div><br></div><div>In Squeak, at: always creates a block, but at:ifAbsent: never creates a block (which means one block activation on error), and the scan is two activations deep.  Note also that findKeyOrNil: wraps around, making more than a single pass if it can't find an element whereas scanFor: makes at most a single pass.</div><div><br></div><div> The Squeak code is much better.  Has the optimization made the code any the less reusable or elegant?  I find it no less reusable and more elegant.  scanFor: is a better thought-out kernel than findKeyOrNil:.  So optimization doesn't always make things less readable, reusable or elegant.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)">OTOH, #associationDeclareAt: and #isOctetString, by their nature, are only of interest to developers.  Users would never use those methods, so optimizing them would be fine I suppose.  Methods which take block arguments also tend to be not as usable by users, since passing a block requres writing some code.  So it seems we have _some_ concrete criteria starting to form simply by having looked at a few examples:<br></div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)"><br></div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)">   - methods which run hundreds of times per minute</div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)">   - methods which take block as arguments</div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)">   - methods which are concerned with internals instead of externals</div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)"><br></div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)">#isAsciiString seems like it could go either way, so I'm fine if you and Eliot prefer it to be optimized.  For me this discussion itself to get a slightly better understanding of each others' positions with the request to keep Dynabook users in mind when evaluating future such optimizations, <span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);float:none;display:inline">is more important</span>.</div><div><br></div><div>Regards,</div>  Chris</div><div class="gmail-HOEnZb"><div class="gmail-h5"><div class="gmail_extra"><br><div class="gmail_quote">On Fri, May 4, 2018 at 2:43 PM, 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-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><span>On Thu, 3 May 2018, Chris Muller wrote:<br>
<br>
</span><span><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
                  On a serious note, libary methods, like that method, ought to be fast,<br>
<br>
<br>
            Every method in the image is a library method, so what do you mean by<br>
<br>
<br>
      Really? Explain me how Browser >> #defaultBrowserTitle, a method I chose randomly, is a library method?<br>
<br>
<br>
Well, I was actually asking for clarification from _you_  for the meaning of "library method", since you were the one assigning an "ought" to them  :).  So I started out with it meaning, "every method in the base image", as a means to open up the question of what you<br>
meant.  My own criteria is "methods which are called thousands of times per minute" _today_.  But as the guy whose done more of these sorts of method optimizations than anyone (most of which I love), I am actually *truly curious* about YOUR criteria on this.  What<br>
methods in the image would Levente prefer to be more readable than performant, and why?<br>
</blockquote>
<br></span>
I can't give you an exact definition of a "library method", but I'm sure that String >> #isAsciiString is one, while Browser >> #defaultBrowserTitle is not.<br>
A rule of thumb could be that if a core method (e.g. anything in Kernel/Collections) is part of an API and it's not there to support a very specific task (e.g. Dictionary >> #associationDeclareAt:), then its performance should be taken into account.<br>
<br>
Btw, please have a look at String >> #isOctetString.<span class="gmail-m_-7395097873584701181HOEnZb"><font color="#888888"><br>
<br>
Levente</font></span><div class="gmail-m_-7395097873584701181HOEnZb"><div class="gmail-m_-7395097873584701181h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
<br>
<br>
<br>
> ....<br>
 <br>
<br>
<br>
                  because you can't know in what situation they will be used. Saying that it's<br>
                  not even used is therefore not a valid reason.<br>
<br>
<br>
            That's only true for the very lowest-level methods.  Generally, it's<br>
            better to design and code for what's known, and NOT try to code for<br>
            the future / unknown.  IF and when that future comes, it'd be easy<br>
            enough for you to deal with any performance issues at THAT time.  In<br>
            the meantime, we "ought"  :) to keep as much beautiful minimal<br>
            elegance as we can.<br>
<br>
<br>
      So, if I decided to write a mail transfer agent, and I found that optimizing #isAsciiString would bump the throughput by, let's say, 10%, then and only then would I be allowed to change the implementation in Squeak? Or should I keep my optimized version in<br>
      my image, because it's not considered generally useful?<br>
<br>
<br>
Yes.  That's when you'll have the concrete context necessary to enable you to make the best implementation choice.  Until then, degrading code readability in exchange for no real-world performance benefit just... well, degrades the code...<br>
</blockquote>
</div></div></blockquote></div><br></div>
</div></div><br><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>