caching more operations in mergeCache, updated comments, added null chk in front of mergeCache ops.

This commit is contained in:
Terence Parr 2012-08-04 11:04:21 -07:00
parent 5455813145
commit b160a3b14d
2 changed files with 34 additions and 5 deletions

View File

@ -103,10 +103,21 @@ import java.util.Set;
simple. By launching multiple threads, we can improve the speed of simple. By launching multiple threads, we can improve the speed of
parsing across a large number of files. parsing across a large number of files.
This strategy is complex because we bounce back and forth from There is no strict ordering between the amount of input used by
the ATN to the DFA, simultaneously performing predictions and SLL vs LL, which makes it really hard to build a cache for full
extending the DFA according to previously unseen input context. Let's say that we have input A B C that leads to an SLL
sequences. conflict with full context X. That implies that using X we
might only use A B but we could also use A B C D to resolve
conflict. Input A B C D could predict alternative 1 in one
position in the input and A B C E could predict alternative 2 in
another position in input. The conflicting SLL configurations
could still be non-unique in the full context prediction, which
would lead us to requiring more input than the original A B C. To
make a prediction cache work, we have to track the exact input used
during the previous prediction. That amounts to a cache that maps X
to a specific DFA for that context.
AVOIDING FULL CONTEXT PREDICTION
We avoid doing full context retry when the outer context is empty, We avoid doing full context retry when the outer context is empty,
we did not dip into the outer context by falling off the end of the we did not dip into the outer context by falling off the end of the
@ -263,6 +274,9 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
* Don't keep around as it wastes huge amounts of memory. DoubleKeyMap * Don't keep around as it wastes huge amounts of memory. DoubleKeyMap
* isn't synchronized but we're ok since two threads shouldn't reuse same * isn't synchronized but we're ok since two threads shouldn't reuse same
* parser/atnsim object because it can only handle one input at a time. * parser/atnsim object because it can only handle one input at a time.
* This maps graphs a and b to merged result c. (a,b)->c. We can avoid
* the merge if we ever see a and b again. Note that (b,a)->c should
* also be examined during cache lookup.
*/ */
protected DoubleKeyMap<PredictionContext,PredictionContext,PredictionContext> mergeCache; protected DoubleKeyMap<PredictionContext,PredictionContext,PredictionContext> mergeCache;

View File

@ -156,8 +156,18 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
boolean rootIsWildcard, boolean rootIsWildcard,
DoubleKeyMap<PredictionContext,PredictionContext,PredictionContext> mergeCache) DoubleKeyMap<PredictionContext,PredictionContext,PredictionContext> mergeCache)
{ {
if ( mergeCache!=null ) {
PredictionContext previous = mergeCache.get(a,b);
if ( previous!=null ) return previous;
previous = mergeCache.get(b,a);
if ( previous!=null ) return previous;
}
PredictionContext rootMerge = mergeRoot(a, b, rootIsWildcard); PredictionContext rootMerge = mergeRoot(a, b, rootIsWildcard);
if ( rootMerge!=null ) return rootMerge; if ( rootMerge!=null ) {
if ( mergeCache!=null ) mergeCache.put(a, b, rootMerge);
return rootMerge;
}
if ( a.invokingState==b.invokingState ) { // a == b if ( a.invokingState==b.invokingState ) { // a == b
PredictionContext parent = merge(a.parent, b.parent, rootIsWildcard, mergeCache); PredictionContext parent = merge(a.parent, b.parent, rootIsWildcard, mergeCache);
@ -169,6 +179,7 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
// of those graphs. dup a, a' points at merged array // of those graphs. dup a, a' points at merged array
// new joined parent so create new singleton pointing to it, a' // new joined parent so create new singleton pointing to it, a'
PredictionContext a_ = new SingletonPredictionContext(parent, a.invokingState); PredictionContext a_ = new SingletonPredictionContext(parent, a.invokingState);
if ( mergeCache!=null ) mergeCache.put(a, b, a_);
return a_; return a_;
} }
else { // a != b payloads differ else { // a != b payloads differ
@ -190,6 +201,7 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
} }
PredictionContext[] parents = {singleParent, singleParent}; PredictionContext[] parents = {singleParent, singleParent};
PredictionContext a_ = new ArrayPredictionContext(parents, payloads); PredictionContext a_ = new ArrayPredictionContext(parents, payloads);
if ( mergeCache!=null ) mergeCache.put(a, b, a_);
return a_; return a_;
} }
// parents differ and can't merge them. Just pack together // parents differ and can't merge them. Just pack together
@ -203,6 +215,7 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
parents = new PredictionContext[] {b.parent, a.parent}; parents = new PredictionContext[] {b.parent, a.parent};
} }
PredictionContext a_ = new ArrayPredictionContext(parents, payloads); PredictionContext a_ = new ArrayPredictionContext(parents, payloads);
if ( mergeCache!=null ) mergeCache.put(a, b, a_);
return a_; return a_;
} }
} }
@ -248,6 +261,8 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
if ( mergeCache!=null ) { if ( mergeCache!=null ) {
PredictionContext previous = mergeCache.get(a,b); PredictionContext previous = mergeCache.get(a,b);
if ( previous!=null ) return previous; if ( previous!=null ) return previous;
previous = mergeCache.get(b,a);
if ( previous!=null ) return previous;
} }
// merge sorted payloads a + b => M // merge sorted payloads a + b => M