This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git


The following commit(s) were added to refs/heads/master by this push:
     new 2b60d54  Optimize nil combinator parsers
2b60d54 is described below

commit 2b60d54cc751672d234893e32f03a4076b0d9f0a
Author: Steve Lawrence <[email protected]>
AuthorDate: Mon Feb 3 12:53:51 2020 -0500

    Optimize nil combinator parsers
    
    The nil combinator currently acts as if it is a ChoiceCombinator, where
    the choices are either a nil parser or a value parser. While this works,
    the Choice combinator has extra overhead due discriminators, multiple
    marks, and extra processor errors to help for good diagnostics. But none
    of this is really needed for the nil combinator
    
    This creates a custom nil combinator that is much simpler and has less
    overhead. Timings show that the SimpleNilOrValueParser went from ~1000us
    to ~600us on average. In a format where almost every element is
    nillable, this saw about a 6-7% increase in performance.
    
    DAFFODIL-1883
---
 .../parsers/NilEmptyCombinatorParsers.scala        | 71 +++++++++++++++++++++-
 1 file changed, 69 insertions(+), 2 deletions(-)

diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/NilEmptyCombinatorParsers.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/NilEmptyCombinatorParsers.scala
index dffa308..309323c 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/NilEmptyCombinatorParsers.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/NilEmptyCombinatorParsers.scala
@@ -17,10 +17,77 @@
 
 package org.apache.daffodil.processors.parsers
 
+import java.io.StringWriter
+import java.io.PrintWriter
+
 import org.apache.daffodil.processors.TermRuntimeData
+import org.apache.daffodil.processors.Success
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.exceptions.UnsuppressableException
+import org.apache.daffodil.dsom.SchemaDefinitionDiagnosticBase
+
+abstract class NilOrValueParser(ctxt: TermRuntimeData, nilParser: Parser, 
valueParser: Parser)
+  extends CombinatorParser(ctxt) {
+
+  override lazy val childProcessors = Vector(nilParser, valueParser)
+  override lazy val runtimeDependencies = Vector()
+
+  def parse(pstate: PState): Unit = {
+    var mark: PState.Mark = pstate.mark("NilOrValueParser")
+    var markLeakCausedByException = false
+
+    try {
+      nilParser.parse1(pstate)
+
+      if (pstate.processorStatus ne Success) {
+        pstate.reset(mark)
+        mark = null
+        valueParser.parse1(pstate)
+      } else {
+        pstate.discard(mark)
+        mark = null
+      }
+
+    } catch {
+      // Similar try/catch/finally logic for returning marks is also used in
+      // the Sequence parser base. The logic isn't
+      // easily factored out so it is duplicated. Changes made here should also
+      // be made there. Only these parsers deal with taking marks, so this 
logic
+      // should not be needed elsewhere.
+      //
+      // TODO: Refactor by hoisting this logic into CombinatorParser using same
+      // technique as in SequenceParserBase's tryParseDetectMarkLeaks and 
parseOne
+      // methods. That way this code really can live in exactly one place.
+      //
+      case t: Throwable => {
+        if (mark != null) {
+          markLeakCausedByException = true
+          if (!t.isInstanceOf[SchemaDefinitionDiagnosticBase] && 
!t.isInstanceOf[UnsuppressableException] && !t.isInstanceOf[java.lang.Error]) {
+            val stackTrace = new StringWriter()
+            t.printStackTrace(new PrintWriter(stackTrace))
+            Assert.invariantFailed("Exception thrown with mark not returned: " 
+ t + "\nStackTrace:\n" + stackTrace)
+          }
+        }
+        throw t
+      }
+    } finally {
+      var markLeak = false;
+      if (mark != null) {
+        pstate.discard(mark)
+        markLeak = true;
+      }
+
+      if (markLeak && !markLeakCausedByException) {
+        // likely a logic bug, throw assertion
+        Assert.invariantFailed("mark not returned, likely a logic bug")
+      }
+    }
+  }
+}
+
 
 case class SimpleNilOrValueParser(ctxt: TermRuntimeData, nilParser: Parser, 
valueParser: Parser)
-  extends ChoiceParser(ctxt, Vector(nilParser, valueParser))
+  extends NilOrValueParser(ctxt, nilParser, valueParser)
 
 case class ComplexNilOrContentParser(ctxt: TermRuntimeData, emptyParser: 
Parser, contentParser: Parser)
-  extends ChoiceParser(ctxt, Vector(emptyParser, contentParser))
+  extends NilOrValueParser(ctxt, emptyParser, contentParser)

Reply via email to