From 057bd36dd8c26439ed87d6b85b1f589d995d0bfb Mon Sep 17 00:00:00 2001 From: "Jim.Idle" Date: Wed, 15 May 2024 09:37:14 -0600 Subject: [PATCH] feat: Catches up minor performance improvements and adds a build tag to build without mutex use when the user knows there will be no multiple access from go routines Signed-off-by: Jim.Idle --- runtime/Go/antlr/go.sum | 2 - runtime/Go/antlr/v4/atn.go | 8 ++-- runtime/Go/antlr/v4/atn_config.go | 3 -- runtime/Go/antlr/v4/jcollect.go | 5 +-- runtime/Go/antlr/v4/ll1_analyzer.go | 1 + runtime/Go/antlr/v4/mutex.go | 9 ++-- runtime/Go/antlr/v4/mutex_nomutex.go | 2 +- runtime/Go/antlr/v4/prediction_context.go | 7 +-- runtime/Go/antlr/v4/statistics.go | 3 +- runtime/Go/antlr/v4/utils.go | 53 +++++++++++++++++++++++ 10 files changed, 69 insertions(+), 24 deletions(-) delete mode 100644 runtime/Go/antlr/go.sum diff --git a/runtime/Go/antlr/go.sum b/runtime/Go/antlr/go.sum deleted file mode 100644 index 2b05f22a47..0000000000 --- a/runtime/Go/antlr/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= diff --git a/runtime/Go/antlr/v4/atn.go b/runtime/Go/antlr/v4/atn.go index cdeefed247..e749ebd0cf 100644 --- a/runtime/Go/antlr/v4/atn.go +++ b/runtime/Go/antlr/v4/atn.go @@ -4,8 +4,6 @@ package antlr -import "sync" - // ATNInvalidAltNumber is used to represent an ALT number that has yet to be calculated or // which is invalid for a particular struct such as [*antlr.BaseRuleContext] var ATNInvalidAltNumber int @@ -56,9 +54,9 @@ type ATN struct { // states []ATNState - mu sync.Mutex - stateMu sync.RWMutex - edgeMu sync.RWMutex + mu Mutex + stateMu RWMutex + edgeMu RWMutex } // NewATN returns a new ATN struct representing the given grammarType and is used diff --git a/runtime/Go/antlr/v4/atn_config.go b/runtime/Go/antlr/v4/atn_config.go index a83f25d349..267308bb3d 100644 --- a/runtime/Go/antlr/v4/atn_config.go +++ b/runtime/Go/antlr/v4/atn_config.go @@ -73,9 +73,6 @@ func NewATNConfig1(c *ATNConfig, state ATNState, context *PredictionContext) *AT // NewATNConfig creates a new ATNConfig instance given an existing config, a state, a context and a semantic context, other 'constructors' // are just wrappers around this one. func NewATNConfig(c *ATNConfig, state ATNState, context *PredictionContext, semanticContext SemanticContext) *ATNConfig { - if semanticContext == nil { - panic("semanticContext cannot be nil") // TODO: Remove this - probably put here for some bug that is now fixed - } b := &ATNConfig{} b.InitATNConfig(c, state, c.GetAlt(), context, semanticContext) b.cType = parserConfig diff --git a/runtime/Go/antlr/v4/jcollect.go b/runtime/Go/antlr/v4/jcollect.go index ceccd96d25..6d668f7983 100644 --- a/runtime/Go/antlr/v4/jcollect.go +++ b/runtime/Go/antlr/v4/jcollect.go @@ -8,7 +8,6 @@ import ( "container/list" "runtime/debug" "sort" - "sync" ) // Collectable is an interface that a struct should implement if it is to be @@ -587,12 +586,12 @@ type VisitRecord struct { type VisitList struct { cache *list.List - lock sync.RWMutex + lock RWMutex } var visitListPool = VisitList{ cache: list.New(), - lock: sync.RWMutex{}, + lock: RWMutex{}, } // NewVisitRecord returns a new VisitRecord instance from the pool if available. diff --git a/runtime/Go/antlr/v4/ll1_analyzer.go b/runtime/Go/antlr/v4/ll1_analyzer.go index 4955ac876f..dfdff000bc 100644 --- a/runtime/Go/antlr/v4/ll1_analyzer.go +++ b/runtime/Go/antlr/v4/ll1_analyzer.go @@ -40,6 +40,7 @@ func (la *LL1Analyzer) getDecisionLookahead(s ATNState) []*IntervalSet { for alt := 0; alt < count; alt++ { look[alt] = NewIntervalSet() + // TODO: This is one of the reasons that ATNConfigs are allocated and freed all the time - fix this tomorrow jim! lookBusy := NewJStore[*ATNConfig, Comparator[*ATNConfig]](aConfEqInst, ClosureBusyCollection, "LL1Analyzer.getDecisionLookahead for lookBusy") la.look1(s.GetTransitions()[alt].getTarget(), nil, BasePredictionContextEMPTY, look[alt], lookBusy, NewBitSet(), false, false) diff --git a/runtime/Go/antlr/v4/mutex.go b/runtime/Go/antlr/v4/mutex.go index c371e28e48..2b0cda4745 100644 --- a/runtime/Go/antlr/v4/mutex.go +++ b/runtime/Go/antlr/v4/mutex.go @@ -1,9 +1,13 @@ -// +build !nomutex +//go:build !antlr.nomutex +// +build !antlr.nomutex package antlr import "sync" +// Mutex is a simple mutex implementation which just delegates to sync.Mutex, it +// is used to provide a mutex implementation for the antlr package, which users +// can turn off with the build tag -tags antlr.nomutex type Mutex struct { mu sync.Mutex } @@ -16,7 +20,6 @@ func (m *Mutex) Unlock() { m.mu.Unlock() } - type RWMutex struct { mu sync.RWMutex } @@ -35,4 +38,4 @@ func (m *RWMutex) RLock() { func (m *RWMutex) RUnlock() { m.mu.RUnlock() -} \ No newline at end of file +} diff --git a/runtime/Go/antlr/v4/mutex_nomutex.go b/runtime/Go/antlr/v4/mutex_nomutex.go index 5893d29fe3..6b9cf4d3cd 100644 --- a/runtime/Go/antlr/v4/mutex_nomutex.go +++ b/runtime/Go/antlr/v4/mutex_nomutex.go @@ -1,4 +1,4 @@ -// +build nomutex +// +build antlr.nomutex package antlr diff --git a/runtime/Go/antlr/v4/prediction_context.go b/runtime/Go/antlr/v4/prediction_context.go index cd89bd3417..a1d5186b8f 100644 --- a/runtime/Go/antlr/v4/prediction_context.go +++ b/runtime/Go/antlr/v4/prediction_context.go @@ -6,7 +6,6 @@ package antlr import ( "fmt" - "golang.org/x/exp/slices" "strconv" ) @@ -144,10 +143,8 @@ func (p *PredictionContext) ArrayEquals(o Collectable[*PredictionContext]) bool // Must compare the actual array elements and not just the array address // - return slices.Equal(p.returnStates, other.returnStates) && - slices.EqualFunc(p.parents, other.parents, func(x, y *PredictionContext) bool { - return x.Equals(y) - }) + return intSlicesEqual(p.returnStates, other.returnStates) && + pcSliceEqual(p.parents, other.parents) } func (p *PredictionContext) SingletonEquals(other Collectable[*PredictionContext]) bool { diff --git a/runtime/Go/antlr/v4/statistics.go b/runtime/Go/antlr/v4/statistics.go index 70c0673a0f..0b2923f760 100644 --- a/runtime/Go/antlr/v4/statistics.go +++ b/runtime/Go/antlr/v4/statistics.go @@ -9,7 +9,6 @@ import ( "path/filepath" "sort" "strconv" - "sync" ) // This file allows the user to collect statistics about the runtime of the ANTLR runtime. It is not enabled by default @@ -30,7 +29,7 @@ type goRunStats struct { // within this package. // jStats []*JStatRec - jStatsLock sync.RWMutex + jStatsLock `RWMutex topN int topNByMax []*JStatRec topNByUsed []*JStatRec diff --git a/runtime/Go/antlr/v4/utils.go b/runtime/Go/antlr/v4/utils.go index 733d7df9dc..36a37f247a 100644 --- a/runtime/Go/antlr/v4/utils.go +++ b/runtime/Go/antlr/v4/utils.go @@ -326,3 +326,56 @@ func isDirectory(dir string) (bool, error) { } return fileInfo.IsDir(), err } + +// intSlicesEqual returns true if the two slices of ints are equal, and is a little +// faster than slices.Equal. +func intSlicesEqual(s1, s2 []int) bool { + if s1 == nil && s2 == nil { + return true + } + if s1 == nil || s2 == nil { + return false + } + if len(s1) == 0 && len(s2) == 0 { + return true + } + + if len(s1) == 0 || len(s2) == 0 || len(s1) != len(s2) { + return false + } + // If the slices are using the same memory, then they are the same slice + if &s1[0] == &s2[0] { + return true + } + for i, v := range s1 { + if v != s2[i] { + return false + } + } + return true +} + +func pcSliceEqual(s1, s2 []*PredictionContext) bool { + if s1 == nil && s2 == nil { + return true + } + if s1 == nil || s2 == nil { + return false + } + if len(s1) == 0 && len(s2) == 0 { + return true + } + if len(s1) == 0 || len(s2) == 0 || len(s1) != len(s2) { + return false + } + // If the slices are using the same memory, then they are the same slice + if &s1[0] == &s2[0] { + return true + } + for i, v := range s1 { + if !v.Equals(s2[i]) { + return false + } + } + return true +}