diff --git a/tool/test/org/antlr/v4/test/TestPerformance.java b/tool/test/org/antlr/v4/test/TestPerformance.java index 5ed8ce804..923ce3737 100644 --- a/tool/test/org/antlr/v4/test/TestPerformance.java +++ b/tool/test/org/antlr/v4/test/TestPerformance.java @@ -55,6 +55,7 @@ import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.dfa.DFA; import org.antlr.v4.runtime.dfa.DFAState; +import org.antlr.v4.runtime.misc.Interval; import org.antlr.v4.runtime.misc.NotNull; import org.antlr.v4.runtime.misc.Nullable; import org.antlr.v4.runtime.misc.ParseCancellationException; @@ -251,6 +252,7 @@ public class TestPerformance extends BaseTest { * required on-the-fly computation. */ private static final boolean COMPUTE_TRANSITION_STATS = false; + private static final boolean SHOW_TRANSITION_STATS_PER_FILE = false; /** * If {@code true}, the transition statistics will be adjusted to a running * total before reporting the final results. @@ -356,6 +358,30 @@ public class TestPerformance extends BaseTest { } } + private static final long[][][] decisionInvocationsPerFile; + private static final long[][][] fullContextFallbackPerFile; + private static final long[][][] nonSllPerFile; + private static final long[][][] totalTransitionsPerDecisionPerFile; + private static final long[][][] computedTransitionsPerDecisionPerFile; + private static final long[][][] fullContextTransitionsPerDecisionPerFile; + static { + if (COMPUTE_TRANSITION_STATS && DETAILED_DFA_STATE_STATS) { + decisionInvocationsPerFile = new long[PASSES][][]; + fullContextFallbackPerFile = new long[PASSES][][]; + nonSllPerFile = new long[PASSES][][]; + totalTransitionsPerDecisionPerFile = new long[PASSES][][]; + computedTransitionsPerDecisionPerFile = new long[PASSES][][]; + fullContextTransitionsPerDecisionPerFile = new long[PASSES][][]; + } else { + decisionInvocationsPerFile = null; + fullContextFallbackPerFile = null; + nonSllPerFile = null; + totalTransitionsPerDecisionPerFile = null; + computedTransitionsPerDecisionPerFile = null; + fullContextTransitionsPerDecisionPerFile = null; + } + } + private static final long[][] timePerFile; private static final int[][] tokensPerFile; static { @@ -398,6 +424,15 @@ public class TestPerformance extends BaseTest { if (COMPUTE_TRANSITION_STATS) { totalTransitionsPerFile[i] = new long[Math.min(sources.size(), MAX_FILES_PER_PARSE_ITERATION)]; computedTransitionsPerFile[i] = new long[Math.min(sources.size(), MAX_FILES_PER_PARSE_ITERATION)]; + + if (DETAILED_DFA_STATE_STATS) { + decisionInvocationsPerFile[i] = new long[Math.min(sources.size(), MAX_FILES_PER_PARSE_ITERATION)][]; + fullContextFallbackPerFile[i] = new long[Math.min(sources.size(), MAX_FILES_PER_PARSE_ITERATION)][]; + nonSllPerFile[i] = new long[Math.min(sources.size(), MAX_FILES_PER_PARSE_ITERATION)][]; + totalTransitionsPerDecisionPerFile[i] = new long[Math.min(sources.size(), MAX_FILES_PER_PARSE_ITERATION)][]; + computedTransitionsPerDecisionPerFile[i] = new long[Math.min(sources.size(), MAX_FILES_PER_PARSE_ITERATION)][]; + fullContextTransitionsPerDecisionPerFile[i] = new long[Math.min(sources.size(), MAX_FILES_PER_PARSE_ITERATION)][]; + } } if (COMPUTE_TIMING_STATS) { @@ -465,7 +500,7 @@ public class TestPerformance extends BaseTest { executorService.shutdown(); executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); - if (COMPUTE_TRANSITION_STATS) { + if (COMPUTE_TRANSITION_STATS && SHOW_TRANSITION_STATS_PER_FILE) { computeTransitionStatistics(); } @@ -804,8 +839,17 @@ public class TestPerformance extends BaseTest { try { FileParseResult fileResult = future.get(); if (COMPUTE_TRANSITION_STATS) { - totalTransitionsPerFile[currentPass][currentIndex] = fileResult.parserTotalTransitions; - computedTransitionsPerFile[currentPass][currentIndex] = fileResult.parserComputedTransitions; + totalTransitionsPerFile[currentPass][currentIndex] = sum(fileResult.parserTotalTransitions); + computedTransitionsPerFile[currentPass][currentIndex] = sum(fileResult.parserComputedTransitions); + + if (DETAILED_DFA_STATE_STATS) { + decisionInvocationsPerFile[currentPass][currentIndex] = fileResult.decisionInvocations; + fullContextFallbackPerFile[currentPass][currentIndex] = fileResult.fullContextFallback; + nonSllPerFile[currentPass][currentIndex] = fileResult.nonSll; + totalTransitionsPerDecisionPerFile[currentPass][currentIndex] = fileResult.parserTotalTransitions; + computedTransitionsPerDecisionPerFile[currentPass][currentIndex] = fileResult.parserComputedTransitions; + fullContextTransitionsPerDecisionPerFile[currentPass][currentIndex] = fileResult.parserFullContextTransitions; + } } if (COMPUTE_TIMING_STATS) { @@ -907,7 +951,13 @@ public class TestPerformance extends BaseTest { System.out.format("There are %d parser DFAState instances, %d configs (%d unique).%n", states, configs, uniqueConfigs.size()); if (DETAILED_DFA_STATE_STATS) { - System.out.format("\tDecision\tStates\tConfigs\tRule%n"); + if (COMPUTE_TRANSITION_STATS) { + System.out.format("\tDecision\tStates\tConfigs\tPredict (ALL)\tPredict (LL)\tNon-SLL\tTransitions\tTransitions (ATN)\tTransitions (LL)\tLA (SLL)\tLA (LL)\tRule%n"); + } + else { + System.out.format("\tDecision\tStates\tConfigs\tRule%n"); + } + for (int i = 0; i < decisionToDFA.length; i++) { DFA dfa = decisionToDFA[i]; if (dfa == null || dfa.states.isEmpty()) { @@ -920,7 +970,57 @@ public class TestPerformance extends BaseTest { } String ruleName = parser.getRuleNames()[parser.getATN().decisionToState.get(dfa.decision).ruleIndex]; - System.out.format("\t%d\t%d\t%d\t%s%n", dfa.decision, dfa.states.size(), decisionConfigs, ruleName); + + long calls = 0; + long fullContextCalls = 0; + long nonSllCalls = 0; + long transitions = 0; + long computedTransitions = 0; + long fullContextTransitions = 0; + double lookahead = 0; + double fullContextLookahead = 0; + String formatString; + if (COMPUTE_TRANSITION_STATS) { + for (long[] data : decisionInvocationsPerFile[currentPass]) { + calls += data[i]; + } + + for (long[] data : fullContextFallbackPerFile[currentPass]) { + fullContextCalls += data[i]; + } + + for (long[] data : nonSllPerFile[currentPass]) { + nonSllCalls += data[i]; + } + + for (long[] data : totalTransitionsPerDecisionPerFile[currentPass]) { + transitions += data[i]; + } + + for (long[] data : computedTransitionsPerDecisionPerFile[currentPass]) { + computedTransitions += data[i]; + } + + for (long[] data : fullContextTransitionsPerDecisionPerFile[currentPass]) { + fullContextTransitions += data[i]; + } + + if (calls > 0) { + lookahead = (double)(transitions - fullContextTransitions) / (double)calls; + } + + if (fullContextCalls > 0) { + fullContextLookahead = (double)fullContextTransitions / (double)fullContextCalls; + } + + formatString = "\t%1$d\t%2$d\t%3$d\t%4$d\t%5$d\t%6$d\t%7$d\t%8$d\t%9$d\t%10$f\t%11$f\t%12$s%n"; + } + else { + calls = 0; + formatString = "\t%1$d\t%2$d\t%3$d\t%12$s%n"; + } + + System.out.format(formatString, dfa.decision, dfa.states.size(), decisionConfigs, calls, fullContextCalls, nonSllCalls, transitions, computedTransitions, fullContextTransitions, lookahead, fullContextLookahead, ruleName); } } } @@ -987,6 +1087,15 @@ public class TestPerformance extends BaseTest { } } + private static long sum(long[] array) { + long result = 0; + for (int i = 0; i < array.length; i++) { + result += array[i]; + } + + return result; + } + protected void compileJavaParser(boolean leftRecursive) throws IOException { String grammarFileName = "Java.g4"; String sourceName = leftRecursive ? "Java-LR.g4" : "Java.g4"; @@ -1275,9 +1384,12 @@ public class TestPerformance extends BaseTest { public final long lexerComputedTransitions; public final int parserDFASize; - public final long parserTotalTransitions; - public final long parserComputedTransitions; - public final long parserFullContextTransitions; + public final long[] decisionInvocations; + public final long[] fullContextFallback; + public final long[] nonSll; + public final long[] parserTotalTransitions; + public final long[] parserComputedTransitions; + public final long[] parserFullContextTransitions; public FileParseResult(String sourceName, int checksum, @Nullable ParseTree parseTree, int tokenCount, long startTime, Lexer lexer, Parser parser) { this.sourceName = sourceName; @@ -1314,13 +1426,19 @@ public class TestPerformance extends BaseTest { if (parser != null) { ParserATNSimulator interpreter = parser.getInterpreter(); if (interpreter instanceof StatisticsParserATNSimulator) { + decisionInvocations = ((StatisticsParserATNSimulator)interpreter).decisionInvocations; + fullContextFallback = ((StatisticsParserATNSimulator)interpreter).fullContextFallback; + nonSll = ((StatisticsParserATNSimulator)interpreter).nonSll; parserTotalTransitions = ((StatisticsParserATNSimulator)interpreter).totalTransitions; parserComputedTransitions = ((StatisticsParserATNSimulator)interpreter).computedTransitions; parserFullContextTransitions = ((StatisticsParserATNSimulator)interpreter).fullContextTransitions; } else { - parserTotalTransitions = 0; - parserComputedTransitions = 0; - parserFullContextTransitions = 0; + decisionInvocations = new long[0]; + fullContextFallback = new long[0]; + nonSll = new long[0]; + parserTotalTransitions = new long[0]; + parserComputedTransitions = new long[0]; + parserFullContextTransitions = new long[0]; } int dfaSize = 0; @@ -1333,9 +1451,12 @@ public class TestPerformance extends BaseTest { parserDFASize = dfaSize; } else { parserDFASize = 0; - parserTotalTransitions = 0; - parserComputedTransitions = 0; - parserFullContextTransitions = 0; + decisionInvocations = new long[0]; + fullContextFallback = new long[0]; + nonSll = new long[0]; + parserTotalTransitions = new long[0]; + parserComputedTransitions = new long[0]; + parserFullContextTransitions = new long[0]; } } } @@ -1368,36 +1489,71 @@ public class TestPerformance extends BaseTest { private static class StatisticsParserATNSimulator extends ParserATNSimulator { - public long totalTransitions; - public long computedTransitions; - public long fullContextTransitions; + public final long[] decisionInvocations; + public final long[] fullContextFallback; + public final long[] nonSll; + public final long[] totalTransitions; + public final long[] computedTransitions; + public final long[] fullContextTransitions; + + private int decision; public StatisticsParserATNSimulator(ATN atn, DFA[] decisionToDFA, PredictionContextCache sharedContextCache) { super(atn, decisionToDFA, sharedContextCache); + decisionInvocations = new long[atn.decisionToState.size()]; + fullContextFallback = new long[atn.decisionToState.size()]; + nonSll = new long[atn.decisionToState.size()]; + totalTransitions = new long[atn.decisionToState.size()]; + computedTransitions = new long[atn.decisionToState.size()]; + fullContextTransitions = new long[atn.decisionToState.size()]; } public StatisticsParserATNSimulator(Parser parser, ATN atn, DFA[] decisionToDFA, PredictionContextCache sharedContextCache) { super(parser, atn, decisionToDFA, sharedContextCache); + decisionInvocations = new long[atn.decisionToState.size()]; + fullContextFallback = new long[atn.decisionToState.size()]; + nonSll = new long[atn.decisionToState.size()]; + totalTransitions = new long[atn.decisionToState.size()]; + computedTransitions = new long[atn.decisionToState.size()]; + fullContextTransitions = new long[atn.decisionToState.size()]; + } + + @Override + public int adaptivePredict(TokenStream input, int decision, ParserRuleContext outerContext) { + try { + this.decision = decision; + decisionInvocations[decision]++; + return super.adaptivePredict(input, decision, outerContext); + } + finally { + this.decision = -1; + } + } + + @Override + protected int execATNWithFullContext(DFA dfa, DFAState D, ATNConfigSet s0, TokenStream input, int startIndex, ParserRuleContext outerContext) { + fullContextFallback[decision]++; + return super.execATNWithFullContext(dfa, D, s0, input, startIndex, outerContext); } @Override protected DFAState getExistingTargetState(DFAState previousD, int t) { - totalTransitions++; + totalTransitions[decision]++; return super.getExistingTargetState(previousD, t); } @Override protected DFAState computeTargetState(DFA dfa, DFAState previousD, int t) { - computedTransitions++; + computedTransitions[decision]++; return super.computeTargetState(dfa, previousD, t); } @Override protected ATNConfigSet computeReachSet(ATNConfigSet closure, int t, boolean fullCtx) { if (fullCtx) { - totalTransitions++; - computedTransitions++; - fullContextTransitions++; + totalTransitions[decision]++; + computedTransitions[decision]++; + fullContextTransitions[decision]++; } return super.computeReachSet(closure, t, fullCtx); @@ -1436,9 +1592,21 @@ public class TestPerformance extends BaseTest { } private static class SummarizingDiagnosticErrorListener extends DiagnosticErrorListener { + private BitSet _sllConflict; + private ATNConfigSet _sllConfigs; @Override public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact, BitSet ambigAlts, ATNConfigSet configs) { + if (COMPUTE_TRANSITION_STATS && DETAILED_DFA_STATE_STATS) { + BitSet sllPredictions = _sllConflict != null ? _sllConflict : getRepresentedAlts(_sllConfigs); + int sllPrediction = sllPredictions.nextSetBit(0); + BitSet llPredictions = ambigAlts != null ? ambigAlts : getRepresentedAlts(configs); + int llPrediction = llPredictions.cardinality() == 0 ? ATN.INVALID_ALT_NUMBER : llPredictions.nextSetBit(0); + if (sllPrediction != llPrediction) { + ((StatisticsParserATNSimulator)recognizer.getInterpreter()).nonSll[dfa.decision]++; + } + } + if (!REPORT_AMBIGUITIES) { return; } @@ -1453,6 +1621,8 @@ public class TestPerformance extends BaseTest { @Override public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) { + _sllConflict = conflictingAlts; + _sllConfigs = configs; if (!REPORT_FULL_CONTEXT) { return; } @@ -1468,6 +1638,14 @@ public class TestPerformance extends BaseTest { @Override public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction, ATNConfigSet configs) { + if (COMPUTE_TRANSITION_STATS && DETAILED_DFA_STATE_STATS) { + BitSet sllPredictions = _sllConflict != null ? _sllConflict : getRepresentedAlts(_sllConfigs); + int sllPrediction = sllPredictions.nextSetBit(0); + if (sllPrediction != prediction) { + ((StatisticsParserATNSimulator)recognizer.getInterpreter()).nonSll[dfa.decision]++; + } + } + if (!REPORT_CONTEXT_SENSITIVITY) { return; }