From 853a95e0b365f21191baf90955fd01b1886032df Mon Sep 17 00:00:00 2001 From: Edd Robinson <me@edd.io> Date: Tue, 24 May 2016 12:25:01 +0100 Subject: [PATCH] Reduce allocations on Query's io.Stringer implementation A query's String method is called multiple times per query. This commit ensures all calls to query.String share use of a strings.NewReplacer. This approximately halves the number of allocations for the benchmarked query. --- CHANGELOG.md | 1 + influxql/ast_test.go | 8 ++++++++ influxql/parser.go | 11 +++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ffc55cd13..fc05849749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - [#6655](https://github.com/influxdata/influxdb/issues/6655): Add HTTP(s) based subscriptions. - [#5906](https://github.com/influxdata/influxdb/issues/5906): Dynamically update the documentation link in the admin UI. - [#6686](https://github.com/influxdata/influxdb/pull/6686): Optimize timestamp run-length decoding +- [#6713](https://github.com/influxdata/influxdb/pull/6713): Reduce allocations during query parsing. ### Bugfixes diff --git a/influxql/ast_test.go b/influxql/ast_test.go index 34cc07f8f3..7a95fbabcf 100644 --- a/influxql/ast_test.go +++ b/influxql/ast_test.go @@ -10,6 +10,14 @@ import ( "github.com/influxdata/influxdb/influxql" ) +func BenchmarkQuery_String(b *testing.B) { + p := influxql.NewParser(strings.NewReader(`SELECT foo AS zoo, a AS b FROM bar WHERE value > 10 AND q = 'hello'`)) + q, _ := p.ParseStatement() + for i := 0; i < b.N; i++ { + _ = q.String() + } +} + // Ensure a value's data type can be retrieved. func TestInspectDataType(t *testing.T) { for i, tt := range []struct { diff --git a/influxql/parser.go b/influxql/parser.go index e8272bb928..7c4c28fd22 100644 --- a/influxql/parser.go +++ b/influxql/parser.go @@ -2680,15 +2680,18 @@ func (p *Parser) parseTokenMaybe(expected Token) bool { return true } +var ( + qsReplacer = strings.NewReplacer("\n", `\n`, `\`, `\\`, `'`, `\'`) + qiReplacer = strings.NewReplacer("\n", `\n`, `\`, `\\`, `"`, `\"`) +) + // QuoteString returns a quoted string. func QuoteString(s string) string { - return `'` + strings.NewReplacer("\n", `\n`, `\`, `\\`, `'`, `\'`).Replace(s) + `'` + return `'` + qsReplacer.Replace(s) + `'` } // QuoteIdent returns a quoted identifier from multiple bare identifiers. func QuoteIdent(segments ...string) string { - r := strings.NewReplacer("\n", `\n`, `\`, `\\`, `"`, `\"`) - var buf bytes.Buffer for i, segment := range segments { needQuote := IdentNeedsQuotes(segment) || @@ -2698,7 +2701,7 @@ func QuoteIdent(segments ...string) string { _ = buf.WriteByte('"') } - _, _ = buf.WriteString(r.Replace(segment)) + _, _ = buf.WriteString(qiReplacer.Replace(segment)) if needQuote { _ = buf.WriteByte('"')