Updated TestPerformance (features, grammar fixes)

This commit is contained in:
Sam Harwell 2012-02-14 07:48:27 -06:00
parent 19afb4a8bc
commit 707311996d
3 changed files with 241 additions and 41 deletions

View File

@ -169,7 +169,6 @@
* Character.isJavaIdentifierPart(int) returns true."
*/
grammar Java;
options {backtrack=true; memoize=true;}
@lexer::members {
protected boolean enumIsKeyword = true;
@ -184,7 +183,9 @@ compilationUnit
( packageDeclaration importDeclaration* typeDeclaration*
| classOrInterfaceDeclaration typeDeclaration*
)
EOF
| packageDeclaration? importDeclaration* typeDeclaration*
EOF
;
packageDeclaration
@ -499,7 +500,7 @@ constructorBody
explicitConstructorInvocation
: nonWildcardTypeArguments? ('this' | 'super') arguments ';'
| expression '.' nonWildcardTypeArguments? 'super' arguments ';'
| primary '.' nonWildcardTypeArguments? 'super' arguments ';'
;
@ -711,17 +712,12 @@ constantExpression
;
expression
: parExpression
| 'this'
| 'super'
| literal
| Identifier
: primary
| expression '.' Identifier
| expression '.' 'class' // should be type.class but causes backtracking
| expression '.' 'this'
| expression '.' 'super' '(' expressionList? ')'
| expression '.' 'super' '.' Identifier arguments?
| expression '.' 'new' Identifier '(' expressionList? ')'
| expression '.' 'super' '.' Identifier arguments?
| expression '.' explicitGenericInvocation
| 'new' creator
| expression '[' expression ']'
@ -754,7 +750,19 @@ expression
| '>' '>' '='<assoc=right>
| '>' '>' '>' '='<assoc=right>
| '<' '<' '='<assoc=right>
| '%='<assoc=right>) expression
| '%='<assoc=right>
)
expression
;
primary
: '(' expression ')'
| 'this'
| 'super'
| literal
| Identifier
| type '.' 'class'
| 'void' '.' 'class'
;
creator
@ -827,15 +835,16 @@ FloatingPointLiteral
| ('0'..'9')+ Exponent FloatTypeSuffix?
| ('0'..'9')+ FloatTypeSuffix
| '0' ('x'|'X')
( HexDigit+ '.' HexDigit* Exponent? FloatTypeSuffix?
| '.' HexDigit+ Exponent? FloatTypeSuffix?
| HexDigit+ Exponent FloatTypeSuffix?
| HexDigit+ FloatTypeSuffix
( HexDigit+ ('.' HexDigit*)? HexExponent FloatTypeSuffix?
| '.' HexDigit+ HexExponent FloatTypeSuffix?
)
;
fragment
Exponent : ('e'|'E'|'p'|'P') ('+'|'-')? ('0'..'9')+ ;
Exponent : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
fragment
HexExponent : ('p'|'P') ('+'|'-')? ('0'..'9')+ ;
fragment
FloatTypeSuffix : ('f'|'F'|'d'|'D') ;
@ -917,13 +926,13 @@ JavaIDDigit
'\u1040'..'\u1049'
;
WS : (' '|'\r'|'\t'|'\u000C'|'\n')+ {$channel=HIDDEN;}
WS : (' '|'\r'|'\t'|'\u000C'|'\n')+ -> channel(HIDDEN)
;
COMMENT
: '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
: '/*' .* '*/' -> channel(HIDDEN)
;
LINE_COMMENT
: '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
: '//' ~('\n'|'\r')* '\r'? '\n' -> channel(HIDDEN)
;

View File

@ -1021,7 +1021,7 @@ WS : (' '|'\r'|'\t'|'\u000C'|'\n')+ {$channel=HIDDEN;}
;
COMMENT
: '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
: '/*' .* '*/' {$channel=HIDDEN;}
;
LINE_COMMENT

View File

@ -1,3 +1,31 @@
/*
* [The "BSD license"]
* Copyright (c) 2012 Sam Harwell
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.test;
import org.antlr.v4.runtime.*;
@ -14,11 +42,19 @@ import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.antlr.v4.runtime.atn.ATNConfig;
import org.antlr.v4.runtime.atn.ParserATNSimulator;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.dfa.DFAState;
public class TestPerformance extends BaseTest {
/** Parse all java files under this package within the JDK_SOURCE_ROOT. */
private static final String TOP_PACKAGE = "java";
private static final String TOP_PACKAGE = "java.lang";
/** True to load java files from sub-packages of {@link #TOP_PACKAGE}. */
private static final boolean RECURSIVE = true;
@ -27,26 +63,41 @@ public class TestPerformance extends BaseTest {
* the standard grammar (Java.g). In either case, the grammar is renamed in the temporary directory to Java.g
* before compiling.
*/
private static final boolean USE_LR_GRAMMAR = false;
private static final boolean USE_LR_GRAMMAR = true;
/**
* True to specify the -Xforceatn option when generating the grammar, forcing all decisions in JavaParser to
* be handled by {@link v2ParserATNSimulator#adaptivePredict}.
* True to specify the -Xforce-atn option when generating the grammar, forcing all decisions in JavaParser to
* be handled by {@link ParserATNSimulator#adaptivePredict}.
*/
private static final boolean FORCE_ATN = false;
/**
* True to specify the -atn option when generating the grammar. This will cause ANTLR
* to export the ATN for each decision as a DOT (GraphViz) file.
*/
private static final boolean EXPORT_ATN_GRAPHS = true;
/**
* True to delete temporary (generated and compiled) files when the test completes.
*/
private static final boolean DELETE_TEMP_FILES = true;
private static final boolean PAUSE_FOR_HEAP_DUMP = false;
/** Parse each file with JavaParser.compilationUnit */
private static final boolean RUN_PARSER = true;
/** True to use {@link BailErrorStrategy}, False to use {@link DefaultErrorStrategy} */
private static final boolean BAIL_ON_ERROR = false;
/** This value is passed to {@link org.antlr.v4.runtime.Parser#setBuildParseTree}. */
private static final boolean BAIL_ON_ERROR = true;
/** This value is passed to {@link Parser#setBuildParseTree}. */
private static final boolean BUILD_PARSE_TREES = false;
/**
* Use ParseTreeWalker.DEFAULT.walk with the BlankJavaParserListener to show parse tree walking overhead.
* If {@link #BUILD_PARSE_TREES} is false, the listener will instead be called during the parsing process via
* {@link org.antlr.v4.runtime.Parser#addParseListener}.
* {@link Parser#addParseListener}.
*/
private static final boolean BLANK_LISTENER = false;
private static final boolean SHOW_DFA_STATE_STATS = true;
private static final boolean SHOW_CONFIG_STATS = false;
/**
* If true, a single JavaLexer will be used, and {@link Lexer#setInputStream} will be called to initialize it
* for each source file. In this mode, the cached DFA will be persisted throughout the lexing process.
@ -67,24 +118,29 @@ public class TestPerformance extends BaseTest {
/** Total number of passes to make over the source */
private static final int PASSES = 4;
private Lexer sharedLexer;
private Parser sharedParser;
private static Lexer sharedLexer;
private static Parser sharedParser;
@SuppressWarnings({"FieldCanBeLocal"})
private ParseTreeListener<Token> sharedListener;
private static ParseTreeListener<Token> sharedListener;
private int tokenCount;
private int currentPass;
@Test
//@Ignore
public void compileJdk() throws IOException {
compileParser(USE_LR_GRAMMAR);
JavaParserFactory factory = getParserFactory();
String jdkSourceRoot = System.getenv("JDK_SOURCE_ROOT");
if (jdkSourceRoot == null) {
jdkSourceRoot = System.getProperty("JDK_SOURCE_ROOT");
}
if (jdkSourceRoot == null) {
System.err.println("The JDK_SOURCE_ROOT environment variable must be set for performance testing.");
return;
}
compileParser(USE_LR_GRAMMAR);
JavaParserFactory factory = getParserFactory();
if (!TOP_PACKAGE.isEmpty()) {
jdkSourceRoot = jdkSourceRoot + '/' + TOP_PACKAGE.replace('.', '/');
}
@ -93,10 +149,13 @@ public class TestPerformance extends BaseTest {
assertTrue(directory.isDirectory());
Collection<CharStream> sources = loadSources(directory, RECURSIVE);
System.out.format("Lex=true, Parse=%s, ForceAtn=%s, Bail=%s, BuildParseTree=%s, BlankListener=%s\n",
RUN_PARSER, FORCE_ATN, BAIL_ON_ERROR, BUILD_PARSE_TREES, BLANK_LISTENER);
System.out.print(getOptionsDescription());
currentPass = 0;
parse1(factory, sources);
for (int i = 0; i < PASSES - 1; i++) {
currentPass = i + 1;
if (CLEAR_DFA) {
sharedLexer = null;
sharedParser = null;
@ -104,6 +163,55 @@ public class TestPerformance extends BaseTest {
parse2(factory, sources);
}
sources.clear();
if (PAUSE_FOR_HEAP_DUMP) {
System.gc();
System.out.println("Pausing before application exit.");
try {
Thread.sleep(4000);
} catch (InterruptedException ex) {
Logger.getLogger(TestPerformance.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@Override
protected void eraseTempDir() {
if (DELETE_TEMP_FILES) {
super.eraseTempDir();
}
}
public static String getOptionsDescription() {
StringBuilder builder = new StringBuilder();
builder.append("Input=");
if (TestPerformance.TOP_PACKAGE.isEmpty()) {
builder.append("*");
}
else {
builder.append(TOP_PACKAGE).append(".*");
}
builder.append(", Grammar=").append(USE_LR_GRAMMAR ? "LR" : "Standard");
builder.append(", ForceAtn=").append(FORCE_ATN);
builder.append('\n');
builder.append("Op=Lex").append(RUN_PARSER ? "+Parse" : " only");
builder.append(", Strategy=").append(BAIL_ON_ERROR ? BailErrorStrategy.class.getSimpleName() : DefaultErrorStrategy.class.getSimpleName());
builder.append(", BuildParseTree=").append(BUILD_PARSE_TREES);
builder.append(", WalkBlankListener=").append(BLANK_LISTENER);
builder.append('\n');
builder.append("Lexer=").append(REUSE_LEXER ? "setInputStream" : "newInstance");
builder.append(", Parser=").append(REUSE_PARSER ? "setInputStream" : "newInstance");
builder.append(", AfterPass=").append(CLEAR_DFA ? "newInstance" : "setInputStream");
builder.append('\n');
return builder.toString();
}
/**
@ -158,6 +266,8 @@ public class TestPerformance extends BaseTest {
}
}
int configOutputSize = 0;
protected void parseSources(JavaParserFactory factory, Collection<CharStream> sources) {
long startTime = System.currentTimeMillis();
tokenCount = 0;
@ -167,7 +277,7 @@ public class TestPerformance extends BaseTest {
input.seek(0);
inputSize += input.size();
// this incurred a great deal of overhead and was causing significant variations in performance results.
//System.out.format("Parsing file %s\n", file.getAbsolutePath());
//System.out.format("Parsing file %s\n", input.getSourceName());
try {
factory.parseFile(input);
} catch (IllegalStateException ex) {
@ -180,6 +290,80 @@ public class TestPerformance extends BaseTest {
inputSize / 1024,
tokenCount,
System.currentTimeMillis() - startTime);
if (RUN_PARSER) {
// make sure the individual DFAState objects actually have unique ATNConfig arrays
final ParserATNSimulator<?> interpreter = sharedParser.getInterpreter();
final DFA[] decisionToDFA = interpreter.decisionToDFA;
if (SHOW_DFA_STATE_STATS) {
int states = 0;
for (int i = 0; i < decisionToDFA.length; i++) {
DFA dfa = decisionToDFA[i];
if (dfa == null || dfa.states == null) {
continue;
}
states += dfa.states.size();
}
System.out.format("There are %d DFAState instances.\n", states);
}
int localDfaCount = 0;
int globalDfaCount = 0;
int localConfigCount = 0;
int globalConfigCount = 0;
int[] contextsInDFAState = new int[0];
for (int i = 0; i < decisionToDFA.length; i++) {
DFA dfa = decisionToDFA[i];
if (dfa == null || dfa.states == null) {
continue;
}
if (SHOW_CONFIG_STATS) {
for (DFAState state : dfa.states.keySet()) {
if (state.configset.size() >= contextsInDFAState.length) {
contextsInDFAState = Arrays.copyOf(contextsInDFAState, state.configset.size() + 1);
}
if (state.isAcceptState) {
boolean hasGlobal = false;
for (ATNConfig config : state.configset) {
if (config.reachesIntoOuterContext > 0) {
globalConfigCount++;
hasGlobal = true;
} else {
localConfigCount++;
}
}
if (hasGlobal) {
globalDfaCount++;
} else {
localDfaCount++;
}
}
contextsInDFAState[state.configset.size()]++;
}
}
}
if (SHOW_CONFIG_STATS && currentPass == 0) {
System.out.format(" DFA accept states: %d total, %d with only local context, %d with a global context\n", localDfaCount + globalDfaCount, localDfaCount, globalDfaCount);
System.out.format(" Config stats: %d total, %d local, %d global\n", localConfigCount + globalConfigCount, localConfigCount, globalConfigCount);
if (SHOW_DFA_STATE_STATS) {
for (int i = 0; i < contextsInDFAState.length; i++) {
if (contextsInDFAState[i] != 0) {
System.out.format(" %d configs = %d\n", i, contextsInDFAState[i]);
}
}
}
}
}
}
protected void compileParser(boolean leftRecursive) throws IOException {
@ -187,8 +371,15 @@ public class TestPerformance extends BaseTest {
String sourceName = leftRecursive ? "Java-LR.g" : "Java.g";
String body = load(sourceName, null);
@SuppressWarnings({"ConstantConditions"})
String[] extraOptions = FORCE_ATN ? new String[] {"-Xforceatn"} : new String[0];
boolean success = rawGenerateAndBuildRecognizer(grammarFileName, body, "JavaParser", "JavaLexer", extraOptions);
List<String> extraOptions = new ArrayList<String>();
if (FORCE_ATN) {
extraOptions.add("-Xforce-atn");
}
if (EXPORT_ATN_GRAPHS) {
extraOptions.add("-atn");
}
String[] extraOptionsArray = extraOptions.toArray(new String[extraOptions.size()]);
boolean success = rawGenerateAndBuildRecognizer(grammarFileName, body, "JavaParser", "JavaLexer", extraOptionsArray);
assertTrue(success);
}
@ -228,7 +419,7 @@ public class TestPerformance extends BaseTest {
final Class<? extends Parser> parserClass = (Class<? extends Parser>)loader.loadClass("JavaParser");
@SuppressWarnings({"unchecked"})
final Class<? extends ParseTreeListener<Token>> listenerClass = (Class<? extends ParseTreeListener<Token>>)loader.loadClass("BlankJavaListener");
this.sharedListener = listenerClass.newInstance();
TestPerformance.sharedListener = listenerClass.newInstance();
final Constructor<? extends Lexer> lexerCtor = lexerClass.getConstructor(CharStream.class);
final Constructor<? extends Parser> parserCtor = parserClass.getConstructor(TokenStream.class);
@ -271,7 +462,7 @@ public class TestPerformance extends BaseTest {
Method parseMethod = parserClass.getMethod("compilationUnit");
Object parseResult = parseMethod.invoke(sharedParser);
assert parseResult instanceof ParseTree;
Assert.assertTrue(parseResult instanceof ParseTree);
if (BUILD_PARSE_TREES && BLANK_LISTENER) {
ParseTreeWalker.DEFAULT.walk(sharedListener, (ParseTree)parseResult);