PromQL: Use a sync.Pool for the generated parser structure (#6591)

* PromQL: Use a sync.Pool for the generated parser structure

The generated PromQL parser allocates a struct about 4kb in size on every run.

This puts a high load on the garbage collector.

To reduce that load, a sync.Pool is used to recycle these structures.

On small queries this makes parsing 2-3 times faster.

Signed-off-by: Tobias Guggenmos <tguggenm@redhat.com>
This commit is contained in:
Tobias Guggenmos 2020-01-09 18:36:13 +01:00 committed by Brian Brazil
parent 863613300d
commit 6534ce843f

View file

@ -19,6 +19,7 @@ import (
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/pkg/errors"
@ -28,8 +29,14 @@ import (
"github.com/prometheus/prometheus/util/strutil"
)
var parserPool = sync.Pool{
New: func() interface{} {
return &parser{}
},
}
type parser struct {
lex *Lexer
lex Lexer
inject ItemType
injecting bool
@ -54,7 +61,7 @@ func (e *ParseErr) Error() string {
// ParseExpr returns the expression parsed from the input.
func ParseExpr(input string) (expr Expr, err error) {
p := newParser(input)
defer parserPool.Put(p)
defer p.recover(&err)
expr = p.parseGenerated(START_EXPRESSION).(Expr)
@ -66,6 +73,7 @@ func ParseExpr(input string) (expr Expr, err error) {
// ParseMetric parses the input into a metric
func ParseMetric(input string) (m labels.Labels, err error) {
p := newParser(input)
defer parserPool.Put(p)
defer p.recover(&err)
return p.parseGenerated(START_METRIC).(labels.Labels), nil
@ -75,6 +83,7 @@ func ParseMetric(input string) (m labels.Labels, err error) {
// label matchers.
func ParseMetricSelector(input string) (m []*labels.Matcher, err error) {
p := newParser(input)
defer parserPool.Put(p)
defer p.recover(&err)
return p.parseGenerated(START_METRIC_SELECTOR).(*VectorSelector).LabelMatchers, nil
@ -82,8 +91,14 @@ func ParseMetricSelector(input string) (m []*labels.Matcher, err error) {
// newParser returns a new parser.
func newParser(input string) *parser {
p := &parser{
lex: Lex(input),
p := parserPool.Get().(*parser)
p.injecting = false
// Clear lexer struct before reusing.
p.lex = Lexer{
input: input,
state: lexStatements,
}
return p
}
@ -112,6 +127,7 @@ func parseSeriesDesc(input string) (labels labels.Labels, values []sequenceValue
p := newParser(input)
p.lex.seriesDesc = true
defer parserPool.Put(p)
defer p.recover(&err)
result := p.parseGenerated(START_SERIES_DESCRIPTION).(*seriesDescription)