From a1e22549452e0df0e9b80afdacb6d7b82c5817e1 Mon Sep 17 00:00:00 2001 From: Alan Shreve Date: Thu, 19 Jan 2023 07:39:08 +0000 Subject: [PATCH] internal/codegen: cache pattern matching compilations Type overrides are checked linearly for every codegen'd type. That linear check currently compiles a regexp pattern for every override that's considered. This commit adds a simple memoizing cache so that each pattern is only ever compiled once. This dramatically improves performance of sqlc for users who have many overrides. ngrok's usage of sqlc generate sees a 10x speedup from this change (4s -> 0.4s). --- internal/pattern/match.go | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/internal/pattern/match.go b/internal/pattern/match.go index 99df56ab1b..1cf8afb1e4 100644 --- a/internal/pattern/match.go +++ b/internal/pattern/match.go @@ -3,6 +3,7 @@ package pattern import ( "fmt" "regexp" + "sync" ) // Match is a wrapper of *regexp.Regexp. @@ -11,9 +12,36 @@ type Match struct { *regexp.Regexp } +var ( + matchCache = make(map[string]*Match) + matchCacheLock sync.RWMutex +) + // Compile takes our match expression as a string, and compiles it into a *Match object. // Will return an error on an invalid pattern. -func MatchCompile(pattern string) (match *Match, err error) { +func MatchCompile(pattern string) (*Match, error) { + // check for pattern in cache + matchCacheLock.RLock() + matcher, ok := matchCache[pattern] + matchCacheLock.RUnlock() + if ok { + return matcher, nil + } + + // pattern isn't in cache, compile it + matcher, err := matchCompile(pattern) + if err != nil { + return nil, err + } + // add it to the cache + matchCacheLock.Lock() + matchCache[pattern] = matcher + matchCacheLock.Unlock() + + return matcher, nil +} + +func matchCompile(pattern string) (match *Match, err error) { regex := "" escaped := false arr := []byte(pattern)