Skip to content

Commit 7a20ec0

Browse files
committed
use sync.Pool for runner pooling
Currently as noted in the comment of `putRunner`, there's no attempt being made to limit the size of the runner pooling - this can result in the pool containing a lot of runners that were once created in a spur but will likely not be used anymore. Instead of trying to do gc within this code, move the pooling to `sync.Pool` which will deallocated objects in idle and therefore keep the size of the pool as small as possible. The pool is on a per-regexp scope, this means certain properties can be re-used for optimal performance. The motivation for this change is that I'm seeing a lot of memory (~300MiB) being hold by these runners until the Go program is restarted which feels like an unoptimal usage of memory, with this change after a spur of these runners have been created in a small amount of time they are gracefully deallocated over time and no longer hold memory indefinitely.
1 parent 5f3687a commit 7a20ec0

File tree

2 files changed

+10
-18
lines changed

2 files changed

+10
-18
lines changed

regexp.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ type Regexp struct {
4747
code *syntax.Code // compiled program
4848

4949
// cache of machines for running regexp
50-
muRun *sync.Mutex
51-
runner []*runner
50+
runner sync.Pool
5251
}
5352

5453
// Compile parses a regular expression and returns, if successful,
@@ -76,7 +75,11 @@ func Compile(expr string, opt RegexOptions) (*Regexp, error) {
7675
capsize: code.Capsize,
7776
code: code,
7877
MatchTimeout: DefaultMatchTimeout,
79-
muRun: &sync.Mutex{},
78+
runner: sync.Pool{
79+
New: func() interface{} {
80+
return new(runner)
81+
},
82+
},
8083
}, nil
8184
}
8285

runner.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,18 +1583,9 @@ func (r *runner) initTrackCount() {
15831583
// It uses the re's runner cache if possible, to avoid
15841584
// unnecessary allocation.
15851585
func (re *Regexp) getRunner() *runner {
1586-
re.muRun.Lock()
1587-
if n := len(re.runner); n > 0 {
1588-
z := re.runner[n-1]
1589-
re.runner = re.runner[:n-1]
1590-
re.muRun.Unlock()
1591-
return z
1592-
}
1593-
re.muRun.Unlock()
1594-
z := &runner{
1595-
re: re,
1596-
code: re.code,
1597-
}
1586+
z := re.runner.Get().(*runner)
1587+
z.re = re
1588+
z.code = re.code
15981589
return z
15991590
}
16001591

@@ -1603,11 +1594,9 @@ func (re *Regexp) getRunner() *runner {
16031594
// grow to the maximum number of simultaneous matches
16041595
// run using re. (The cache empties when re gets garbage collected.)
16051596
func (re *Regexp) putRunner(r *runner) {
1606-
re.muRun.Lock()
16071597
r.runtext = nil
16081598
if r.runmatch != nil {
16091599
r.runmatch.text = nil
16101600
}
1611-
re.runner = append(re.runner, r)
1612-
re.muRun.Unlock()
1601+
re.runner.Put(r)
16131602
}

0 commit comments

Comments
 (0)