Skip to content

Commit

Permalink
Merge pull request #8652 from influxdata/sgc-literal-cursor
Browse files Browse the repository at this point in the history
Reduce allocations using nil cursors and literal value cursors
  • Loading branch information
stuartcarnie authored Aug 1, 2017
2 parents 68340c2 + 6311a19 commit 5449285
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 135 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [#8426](https://github.com/influxdata/influxdb/issues/8426): Add `parse-multivalue-plugin` to allow users to choose how multivalue plugins should be handled by the collectd service.
- [#8548](https://github.com/influxdata/influxdb/issues/8548): Allow panic recovery to be disabled when investigating server issues.
- [#8525](https://github.com/influxdata/influxdb/issues/8525): Support http pipelining for /query endpoint.
- [#8652](https://github.com/influxdata/influxdb/pull/8652): Reduce allocations when reading data

### Bugfixes

Expand Down
28 changes: 14 additions & 14 deletions tsdb/engine/tsm1/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -1852,29 +1852,29 @@ func (e *Engine) createVarRefSeriesIterator(ref *influxql.VarRef, name string, s
// If a field was requested, use a nil cursor of the requested type.
switch ref.Type {
case influxql.Float, influxql.AnyField:
aux[i] = &floatNilLiteralCursor{}
aux[i] = nilFloatLiteralValueCursor
continue
case influxql.Integer:
aux[i] = &integerNilLiteralCursor{}
aux[i] = nilIntegerLiteralValueCursor
continue
case influxql.Unsigned:
aux[i] = &unsignedNilLiteralCursor{}
aux[i] = nilUnsignedLiteralValueCursor
continue
case influxql.String:
aux[i] = &stringNilLiteralCursor{}
aux[i] = nilStringLiteralValueCursor
continue
case influxql.Boolean:
aux[i] = &booleanNilLiteralCursor{}
aux[i] = nilBooleanLiteralValueCursor
continue
}
}

// If field doesn't exist, use the tag value.
if v := tags.Value(ref.Val); v == "" {
// However, if the tag value is blank then return a null.
aux[i] = &stringNilLiteralCursor{}
aux[i] = nilStringLiteralValueCursor
} else {
aux[i] = &stringLiteralCursor{value: v}
aux[i] = &literalValueCursor{value: v}
}
}
}
Expand All @@ -1896,29 +1896,29 @@ func (e *Engine) createVarRefSeriesIterator(ref *influxql.VarRef, name string, s
// If a field was requested, use a nil cursor of the requested type.
switch ref.Type {
case influxql.Float, influxql.AnyField:
conds[i] = &floatNilLiteralCursor{}
conds[i] = nilFloatLiteralValueCursor
continue
case influxql.Integer:
conds[i] = &integerNilLiteralCursor{}
conds[i] = nilIntegerLiteralValueCursor
continue
case influxql.Unsigned:
conds[i] = &unsignedNilLiteralCursor{}
conds[i] = nilUnsignedLiteralValueCursor
continue
case influxql.String:
conds[i] = &stringNilLiteralCursor{}
conds[i] = nilStringLiteralValueCursor
continue
case influxql.Boolean:
conds[i] = &booleanNilLiteralCursor{}
conds[i] = nilBooleanLiteralValueCursor
continue
}
}

// If field doesn't exist, use the tag value.
if v := tags.Value(ref.Val); v == "" {
// However, if the tag value is blank then return a null.
conds[i] = &stringNilLiteralCursor{}
conds[i] = nilStringLiteralValueCursor
} else {
conds[i] = &stringLiteralCursor{value: v}
conds[i] = &literalValueCursor{value: v}
}
}
}
Expand Down
100 changes: 0 additions & 100 deletions tsdb/engine/tsm1/iterator.gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,26 +532,6 @@ func (c *floatDescendingCursor) nextTSM() {
}
}

// floatLiteralCursor represents a cursor that always returns a single value.
// It doesn't not have a time value so it can only be used with nextAt().
type floatLiteralCursor struct {
value float64
}

func (c *floatLiteralCursor) close() error { return nil }
func (c *floatLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *floatLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *floatLiteralCursor) nextAt(seek int64) interface{} { return c.value }

// floatNilLiteralCursor represents a cursor that always returns a typed nil value.
// It doesn't not have a time value so it can only be used with nextAt().
type floatNilLiteralCursor struct{}

func (c *floatNilLiteralCursor) close() error { return nil }
func (c *floatNilLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, (*float64)(nil) }
func (c *floatNilLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, (*float64)(nil) }
func (c *floatNilLiteralCursor) nextAt(seek int64) interface{} { return (*float64)(nil) }

type integerIterator struct {
cur integerCursor
aux []cursorAt
Expand Down Expand Up @@ -967,26 +947,6 @@ func (c *integerDescendingCursor) nextTSM() {
}
}

// integerLiteralCursor represents a cursor that always returns a single value.
// It doesn't not have a time value so it can only be used with nextAt().
type integerLiteralCursor struct {
value int64
}

func (c *integerLiteralCursor) close() error { return nil }
func (c *integerLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *integerLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *integerLiteralCursor) nextAt(seek int64) interface{} { return c.value }

// integerNilLiteralCursor represents a cursor that always returns a typed nil value.
// It doesn't not have a time value so it can only be used with nextAt().
type integerNilLiteralCursor struct{}

func (c *integerNilLiteralCursor) close() error { return nil }
func (c *integerNilLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, (*int64)(nil) }
func (c *integerNilLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, (*int64)(nil) }
func (c *integerNilLiteralCursor) nextAt(seek int64) interface{} { return (*int64)(nil) }

type unsignedIterator struct {
cur unsignedCursor
aux []cursorAt
Expand Down Expand Up @@ -1402,26 +1362,6 @@ func (c *unsignedDescendingCursor) nextTSM() {
}
}

// unsignedLiteralCursor represents a cursor that always returns a single value.
// It doesn't not have a time value so it can only be used with nextAt().
type unsignedLiteralCursor struct {
value uint64
}

func (c *unsignedLiteralCursor) close() error { return nil }
func (c *unsignedLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *unsignedLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *unsignedLiteralCursor) nextAt(seek int64) interface{} { return c.value }

// unsignedNilLiteralCursor represents a cursor that always returns a typed nil value.
// It doesn't not have a time value so it can only be used with nextAt().
type unsignedNilLiteralCursor struct{}

func (c *unsignedNilLiteralCursor) close() error { return nil }
func (c *unsignedNilLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, (*uint64)(nil) }
func (c *unsignedNilLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, (*uint64)(nil) }
func (c *unsignedNilLiteralCursor) nextAt(seek int64) interface{} { return (*uint64)(nil) }

type stringIterator struct {
cur stringCursor
aux []cursorAt
Expand Down Expand Up @@ -1837,26 +1777,6 @@ func (c *stringDescendingCursor) nextTSM() {
}
}

// stringLiteralCursor represents a cursor that always returns a single value.
// It doesn't not have a time value so it can only be used with nextAt().
type stringLiteralCursor struct {
value string
}

func (c *stringLiteralCursor) close() error { return nil }
func (c *stringLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *stringLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *stringLiteralCursor) nextAt(seek int64) interface{} { return c.value }

// stringNilLiteralCursor represents a cursor that always returns a typed nil value.
// It doesn't not have a time value so it can only be used with nextAt().
type stringNilLiteralCursor struct{}

func (c *stringNilLiteralCursor) close() error { return nil }
func (c *stringNilLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, (*string)(nil) }
func (c *stringNilLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, (*string)(nil) }
func (c *stringNilLiteralCursor) nextAt(seek int64) interface{} { return (*string)(nil) }

type booleanIterator struct {
cur booleanCursor
aux []cursorAt
Expand Down Expand Up @@ -2272,24 +2192,4 @@ func (c *booleanDescendingCursor) nextTSM() {
}
}

// booleanLiteralCursor represents a cursor that always returns a single value.
// It doesn't not have a time value so it can only be used with nextAt().
type booleanLiteralCursor struct {
value bool
}

func (c *booleanLiteralCursor) close() error { return nil }
func (c *booleanLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *booleanLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *booleanLiteralCursor) nextAt(seek int64) interface{} { return c.value }

// booleanNilLiteralCursor represents a cursor that always returns a typed nil value.
// It doesn't not have a time value so it can only be used with nextAt().
type booleanNilLiteralCursor struct{}

func (c *booleanNilLiteralCursor) close() error { return nil }
func (c *booleanNilLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, (*bool)(nil) }
func (c *booleanNilLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, (*bool)(nil) }
func (c *booleanNilLiteralCursor) nextAt(seek int64) interface{} { return (*bool)(nil) }

var _ = fmt.Print
21 changes: 0 additions & 21 deletions tsdb/engine/tsm1/iterator.gen.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -528,27 +528,6 @@ func (c *{{.name}}DescendingCursor) nextTSM() {
}
}

// {{.name}}LiteralCursor represents a cursor that always returns a single value.
// It doesn't not have a time value so it can only be used with nextAt().
type {{.name}}LiteralCursor struct {
value {{.Type}}
}

func (c *{{.name}}LiteralCursor) close() error { return nil }
func (c *{{.name}}LiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *{{.name}}LiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *{{.name}}LiteralCursor) nextAt(seek int64) interface{} { return c.value }


// {{.name}}NilLiteralCursor represents a cursor that always returns a typed nil value.
// It doesn't not have a time value so it can only be used with nextAt().
type {{.name}}NilLiteralCursor struct {}

func (c *{{.name}}NilLiteralCursor) close() error { return nil }
func (c *{{.name}}NilLiteralCursor) peek() (t int64, v interface{}) { return tsdb.EOF, (*{{.Type}})(nil) }
func (c *{{.name}}NilLiteralCursor) next() (t int64, v interface{}) { return tsdb.EOF, (*{{.Type}})(nil) }
func (c *{{.name}}NilLiteralCursor) nextAt(seek int64) interface{} { return (*{{.Type}})(nil) }

{{end}}

var _ = fmt.Print
41 changes: 41 additions & 0 deletions tsdb/engine/tsm1/iterator.get_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package tsm1

import (
"testing"

"github.com/influxdata/influxdb/influxql"
)

func BenchmarkIntegerIterator_Next(b *testing.B) {
opt := influxql.IteratorOptions{
Aux: []influxql.VarRef{{Val: "f1"}, {Val: "f1"}, {Val: "f1"}, {Val: "f1"}},
}
aux := []cursorAt{
&literalValueCursor{value: "foo bar"},
&literalValueCursor{value: int64(1e3)},
&literalValueCursor{value: float64(1e3)},
&literalValueCursor{value: true},
}

cur := newIntegerIterator("m0", influxql.Tags{}, opt, &infiniteIntegerCursor{}, aux, nil, nil)

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
cur.Next()
}
}

type infiniteIntegerCursor struct{}

func (*infiniteIntegerCursor) close() error {
return nil
}

func (*infiniteIntegerCursor) next() (t int64, v interface{}) {
return 0, 0
}

func (*infiniteIntegerCursor) nextInteger() (t int64, v int64) {
return 0, 0
}
21 changes: 21 additions & 0 deletions tsdb/engine/tsm1/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/influxdata/influxdb/influxql"
"github.com/influxdata/influxdb/tsdb"
)

func newLimitIterator(input influxql.Iterator, opt influxql.IteratorOptions) influxql.Iterator {
Expand Down Expand Up @@ -100,3 +101,23 @@ func (c *unsignedCastIntegerCursor) nextUnsigned() (int64, uint64) {
t, v := c.cursor.nextInteger()
return t, uint64(v)
}

// literalValueCursor represents a cursor that always returns a single value.
// It doesn't not have a time value so it can only be used with nextAt().
type literalValueCursor struct {
value interface{}
}

func (c *literalValueCursor) close() error { return nil }
func (c *literalValueCursor) peek() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *literalValueCursor) next() (t int64, v interface{}) { return tsdb.EOF, c.value }
func (c *literalValueCursor) nextAt(seek int64) interface{} { return c.value }

// preallocate and cast to cursorAt to avoid allocations
var (
nilFloatLiteralValueCursor cursorAt = &literalValueCursor{value: (*float64)(nil)}
nilIntegerLiteralValueCursor cursorAt = &literalValueCursor{value: (*int64)(nil)}
nilUnsignedLiteralValueCursor cursorAt = &literalValueCursor{value: (*uint64)(nil)}
nilStringLiteralValueCursor cursorAt = &literalValueCursor{value: (*string)(nil)}
nilBooleanLiteralValueCursor cursorAt = &literalValueCursor{value: (*bool)(nil)}
)

0 comments on commit 5449285

Please sign in to comment.