<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Ah, yes. I hadn't even thought about that. Probably because I've
      only ever used Squeak on 64bits with immediate floats ^^.</p>
    <p>I followed your suggestion and implemented a Matrix2x3 in pure
      Smalltalk.<br>
      It's actually really fast and surprisingly even beats the existing
      implementation in quite a few cases. :D<br>
      Most notably, the plugin-supported point transformation (<i>localPointToGlobal:</i>)
      is actually slower on my machine.<br>
      Only the transformation of multiple points at a time, as done in <i>localBoundsToGlobal:</i>
      is significantly (~3x) faster using the plugin.</p>
    <p>Below are some of the measurements I have taken in a 64bit trunk
      image using squeak.cog.spur_linux64x64:</p>
    <blockquote>
      <p><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>" localPointToGlobal: "</tt><tt><br>
        </tt><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>[mat2x3Old localPointToGlobal: -10 @ 10] bench.</tt><tt><br>
        </tt><tt>" '11,700,000 per second. 85.6 nanoseconds per run.
          1.43971 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[mat2x3Old transformPoint: -10 @ 10] bench.</tt><tt><br>
        </tt><tt>" '2,330,000 per second. 429 nanoseconds per run.
          0.37985 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[mat2x3New localPointToGlobal: -10 @ 10] bench.</tt><tt><br>
        </tt><tt>" '12,500,000 per second. 80.3 nanoseconds per run.
          1.89962 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[morphic localPointToGlobal: -10 @ 10] bench.</tt><tt><br>
        </tt><tt>" '2,710,000 per second. 370 nanoseconds per run. 1.16
          % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>" localBoundsToGlobal: "</tt><tt><br>
        </tt><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>[mat2x3Old localBoundsToGlobal: rect] bench.</tt><tt><br>
        </tt><tt>" '6,770,000 per second. 148 nanoseconds per run.
          1.67966 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[mat2x3New localBoundsToGlobal: rect] bench.</tt><tt><br>
        </tt><tt>" '2,090,000 per second. 480 nanoseconds per run.
          1.55969 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[morphic localBoundsToGlobal: rect] bench.</tt><tt><br>
        </tt><tt>" '505,000 per second. 1.98 microseconds per run.
          1.95922 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>" localBoundsToGlobal: (pure translation) "</tt><tt><br>
        </tt><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>[mat2x3OldTranslation localBoundsToGlobal: rect] bench.</tt><tt><br>
        </tt><tt>" '6,780,000 per second. 147 nanoseconds per run. 1.7 %
          GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[mat2x3NewTranslation localBoundsToGlobal: rect] bench.</tt><tt><br>
        </tt><tt>" '5,860,000 per second. 171 nanoseconds per run. 1.48
          % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[morphicTranslation localBoundsToGlobal: rect] bench.</tt><tt><br>
        </tt><tt>" '1,580,000 per second. 631 nanoseconds per run. 4.12
          % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>"composedWithLocal:"</tt><tt><br>
        </tt><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>[mat2x3Old composedWithLocal: mat2x3OldRotation] bench.</tt><tt><br>
        </tt><tt>" '9,670,000 per second. 103 nanoseconds per run.
          1.19976 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[mat2x3New composedWithLocal: mat2x3NewRotation] bench.</tt><tt><br>
        </tt><tt>" '6,920,000 per second. 144 nanoseconds per run.
          1.4997 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[morphic composedWithLocal: morphicRotation] bench.</tt><tt><br>
        </tt><tt>" '11,100,000 per second. 89.8 nanoseconds per run.
          1.09978 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>" instance creation "</tt><tt><br>
        </tt><tt>"--------------------------------"</tt><tt><br>
        </tt><tt>[MatrixTransform2x3 withOffset: offset] bench.</tt><tt><br>
        </tt><tt>" '3,320,000 per second. 301 nanoseconds per run.
          1.91962 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[Matrix2x3 withOffset: offset] bench.</tt><tt><br>
        </tt><tt>" '24,800,000 per second. 40.3 nanoseconds per run.
          10.63787 % GC time.'"</tt><tt><br>
        </tt><tt><br>
        </tt><tt>[MorphicTransform offset: offset] bench.</tt><tt><br>
        </tt><tt>" '43,600,000 per second. 22.9 nanoseconds per run.
          7.63847 % GC time.'"</tt><br>
      </p>
    </blockquote>
    <p><br>
    </p>
    <p>There are quite a few more benchmarks in the attached file.<br>
      I have also attached a change set of the implementation I used, so
      you can try it out for yourselves if you'd like :)<br>
      <br>
      Cheers<br>
      Stephan<br>
      <br>
    </p>
    <div class="moz-cite-prefix">On 28.07.20 19:33, Vanessa Freudenberg
      wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:CAOWGXNBs2bX6dXhjuf7ss3NCxm10hNvrj2D+40f9WvOHG=6CFQ@mail.gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <div dir="ltr">
        <div dir="ltr">On Tue, Jul 28, 2020 at 4:34 AM Stephan Lutz <<a
            href="mailto:dev@stlutz.net" moz-do-not-send="true">dev@stlutz.net</a>>
          wrote:<br>
        </div>
        <div class="gmail_quote">
          <blockquote class="gmail_quote" style="margin:0px 0px 0px
            0.8ex;border-left:1px solid
            rgb(204,204,204);padding-left:1ex">
            <div>
              <p>While transforming points using MatrixTransform2x3 we
                noticed some strange rounding behavior:</p>
              <blockquote>
                <p><tt>"with plugin"</tt><tt><br>
                  </tt><tt>(MatrixTransform2x3 withOffset: 5 @ 10)
                    localPointToGlobal: 0@0. "5@10"</tt><tt><br>
                  </tt><tt>(MatrixTransform2x3 withOffset: -5 @ -10)
                    localPointToGlobal: 0@0. "-4@ -9"</tt><tt><br>
                  </tt><tt><br>
                  </tt><tt>"without plugin"</tt><tt><br>
                  </tt><tt>((MatrixTransform2x3 withOffset: 5 @ 10)
                    transformPoint: 0@0) rounded. "5@10"</tt><tt><br>
                  </tt><tt>((MatrixTransform2x3 withOffset: -5 @ -10)
                    transformPoint: 0@0) rounded. "-5@ -10"</tt></p>
              </blockquote>
              <p>It appears the code used to round in the plugin simply
                adds 0.5 and truncates the result, which does not work
                correctly for negative numbers.<br>
                This code can be found in <tt>Matrix2x3Plugin >>
                  #roundAndStoreResultPoint:</tt> and <tt>Matrix2x3Plugin
                  >> #roundAndStoreResultRect:x0:y0:x1:y1:</tt> .</p>
              <p>----</p>
              <p>On a kind of related note: Is there even a reason to
                round the resulting floats?</p>
              <p>While the class comment of MatrixTransform2x3 notes
                that this behavior is intentional, glancing quickly over
                its uses we could not find anything taking advantage or
                benefiting from it. It's also not a limitation of the
                DisplayTransform interface, since MorphicTransform does
                produce floating point values. Wouldn't it be much more
                versatile and easier to leave rounding to users if they
                actually need it?</p>
            </div>
          </blockquote>
          <div>No. Having a float result means that the primitive would
            need to allocate two Float objects. Any allocation can fail
            due to memory exhaustion. So the primitive would have to be
            made to retry the allocation after running a garbage
            collection.</div>
          <div><br>
          </div>
          <div>Secondly, its results are primarily used to set up a
            WarpBlt IIRC, for drawing rotated user objects in Etoys.
            WarpBlt fails if the coords are not integers. The failure
            code rounds the numbers and retries. Doing the rounding in
            the matrix primitives ensured a fast path to rendering -
            that's why it was done that way.</div>
          <div><br>
          </div>
          <div>So, there are very good reasons why the plugin returns
            integers. And there are Squeak VMs where this still is a
            very reasonable behavior. It also would be a good idea to
            document the reasoning in the class comment
            of MatrixTransform2x3.</div>
          <div><br>
          </div>
          <div>That being said, there is virtually no reason to use it
            when running on Cog, much less Sista, especially on 64 bits
            where we have immediate floats. An interesting thing would
            be to compare a pure Smalltalk implementation to the
            performance of the plugin. If you need floating point
            transform results, just write it in Smalltalk, would be my
            suggestion.</div>
          <div><br>
          </div>
          <div>- Vanessa -</div>
        </div>
      </div>
      <br>
      <fieldset class="mimeAttachmentHeader"></fieldset>
      <pre class="moz-quote-pre" wrap="">
</pre>
    </blockquote>
  </body>
</html>