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