Uploaded image for project: 'Daffodil'
  1. Daffodil
  2. DFDL-1425

Performance: convert to using scala 2.11 macros

XMLWordPrintableJSON

    • Icon: Improvement Improvement
    • Resolution: Fixed
    • Icon: Normal Normal
    • 2.0.0
    • None
    • Back End
    • None

      Many things use "downward funargs", that is, things that pass a function down into another function. Classic example is the scala map function on collections, but any use of call-by-name also seems to generate these closures.

      This results in heap allocation of a closure object, and so takes code that would not allocate, and makes it allocate things.

      To fix this either one must crank up optimizations so more aggressive inlining takes place (which interferes with debugging), or convert these places to macros instead of higher-order functions.

      (Edited: see later comment - this macro approach doesn't work in scala 2.11 for map/foreach because the function object passed takes an argument. Where a zero-argument body expression is passed this approach works, but not if an actual function object is passed.)

      Places where this is needed:

      Assert.usage and Assert.invariant

      Logging (see DFDL1242)

      all the "with body" functions. Search code for "def with.*" to pull up all the with-body definitions.

      OnStack and LocalStack idioms that pop the stack and push the stack automatically.
      (Note: OnStack and LocalStack take function arguments that need an argument. We avoid allocation there by aggressive inlining, not by way of macros)

      Maybe.map (Note: uses inlining,not macros)

      EntityReplacer (note: uses inlining not macros)

      To avoid code-size blow-ups, some refactoring may help:

      def withFooBar(body: => Bar) = {
           // bunch of preliminary stuff
           try {
               // protected stuff
               body // call the body function
               // more protected stuff
           } catch {
            /// bunch of catch cases 
           } finally {
             /// finally stuff
          }
          /// wrap up.
      }

      Imagine that where the comments are above, there are blocks of code that are fairly long
      and complex.

      As a macro, it will want to avoid repetition of these code blocks, so like:

      def withFooBar(body : => Bar) = macro (body) => q"""
          {
             preliminaryStuff()
           try {
               protected1Stuff()
               $body // call the body function
               protected2Stuff()
           } catch {
             case th: Throwable => handleThrow(th)
           } finally {
             handleFinally()
          }
          wrapUp()
          }"""

      In the above, the blocks of code that were in the original definition are factored out into callable methods (if they contain any significant quantity of code. Otherwise they can stay as is.)
      This avoids repetition of more than just the callouts and try/catch/finally structure.
      Note that any time a function is defined on a final class it can be inlined so long as it is not used polymorphically. This can be preferable to converting it into a macro.

              dthompson David Thompson
              mbeckerle.dfdl Mike Beckerle
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Created:
                Updated:
                Resolved: