Skip to content

Commit

Permalink
Merge pull request #2131 from influxdb/marshalTags
Browse files Browse the repository at this point in the history
Optimize marshalTags()
  • Loading branch information
benbjohnson committed Mar 31, 2015
2 parents 94a4baf + 04a475c commit 58cd43d
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 9 deletions.
37 changes: 28 additions & 9 deletions database.go
Original file line number Diff line number Diff line change
Expand Up @@ -1308,18 +1308,37 @@ func (db *database) continuousQueryByName(name string) *ContinuousQuery {

// used to convert the tag set to bytes for use as a lookup key
func marshalTags(tags map[string]string) []byte {
s := make([]string, 0, len(tags))
// pull out keys to sort
for k := range tags {
s = append(s, k)
// Empty maps marshal to empty bytes.
if len(tags) == 0 {
return nil
}
sort.Strings(s)

// now append on the key values in key sorted order
for _, k := range s {
s = append(s, tags[k])
// Extract keys and determine final size.
sz := (len(tags) * 2) - 1 // separators
keys := make([]string, 0, len(tags))
for k, v := range tags {
keys = append(keys, k)
sz += len(k) + len(v)
}
sort.Strings(keys)

// Generate marshaled bytes.
b := make([]byte, sz)
buf := b
for _, k := range keys {
copy(buf, k)
buf[len(k)] = '|'
buf = buf[len(k)+1:]
}
for i, k := range keys {
v := tags[k]
copy(buf, v)
if i < len(keys)-1 {
buf[len(v)] = '|'
buf = buf[len(v)+1:]
}
}
return []byte(strings.Join(s, "|"))
return b
}

// timeBetweenInclusive returns true if t is between min and max, inclusive.
Expand Down
49 changes: 49 additions & 0 deletions internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package influxdb
// This file is run within the "influxdb" package and allows for internal unit tests.

import (
"bytes"
"fmt"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -310,6 +312,53 @@ func TestShardGroup_Contains(t *testing.T) {
}
}

// Ensure tags can be marshaled into a byte slice.
func TestMarshalTags(t *testing.T) {
for i, tt := range []struct {
tags map[string]string
result []byte
}{
{
tags: nil,
result: nil,
},
{
tags: map[string]string{"foo": "bar"},
result: []byte(`foo|bar`),
},
{
tags: map[string]string{"foo": "bar", "baz": "battttt"},
result: []byte(`baz|foo|battttt|bar`),
},
} {
result := marshalTags(tt.tags)
if !bytes.Equal(result, tt.result) {
t.Fatalf("%d. unexpected result: exp=%s, got=%s", i, tt.result, result)
}
}
}

func BenchmarkMarshalTags_KeyN1(b *testing.B) { benchmarkMarshalTags(b, 1) }
func BenchmarkMarshalTags_KeyN3(b *testing.B) { benchmarkMarshalTags(b, 3) }
func BenchmarkMarshalTags_KeyN5(b *testing.B) { benchmarkMarshalTags(b, 5) }
func BenchmarkMarshalTags_KeyN10(b *testing.B) { benchmarkMarshalTags(b, 10) }

func benchmarkMarshalTags(b *testing.B, keyN int) {
const keySize, valueSize = 8, 15

// Generate tag map.
tags := make(map[string]string)
for i := 0; i < keyN; i++ {
tags[fmt.Sprintf("%0*d", keySize, i)] = fmt.Sprintf("%0*d", valueSize, i)
}

// Unmarshal map into byte slice.
b.ReportAllocs()
for i := 0; i < b.N; i++ {
marshalTags(tags)
}
}

// MustParseExpr parses an expression string and returns its AST representation.
func MustParseExpr(s string) influxql.Expr {
expr, err := influxql.ParseExpr(s)
Expand Down

0 comments on commit 58cd43d

Please sign in to comment.