diff --git a/spanner/value_benchmarks_test.go b/spanner/value_benchmarks_test.go new file mode 100644 index 000000000000..cec523d19ff0 --- /dev/null +++ b/spanner/value_benchmarks_test.go @@ -0,0 +1,210 @@ +/* +Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +build go1.7 +// for sub-benchmarks + +package spanner + +import ( + "reflect" + "strconv" + "testing" + + "cloud.google.com/go/civil" + proto3 "github.com/golang/protobuf/ptypes/struct" + sppb "google.golang.org/genproto/googleapis/spanner/v1" +) + +func BenchmarkEncodeIntArray(b *testing.B) { + for _, s := range []struct { + name string + f func(a []int) (*proto3.Value, *sppb.Type, error) + }{ + {"Orig", encodeIntArrayOrig}, + {"Func", encodeIntArrayFunc}, + {"Reflect", encodeIntArrayReflect}, + } { + b.Run(s.name, func(b *testing.B) { + for _, size := range []int{1, 10, 100, 1000} { + a := make([]int, size) + b.Run(strconv.Itoa(size), func(b *testing.B) { + for i := 0; i < b.N; i++ { + s.f(a) + } + }) + } + }) + } +} + +func encodeIntArrayOrig(a []int) (*proto3.Value, *sppb.Type, error) { + vs := make([]*proto3.Value, len(a)) + var err error + for i := range a { + vs[i], _, err = encodeValue(a[i]) + if err != nil { + return nil, nil, err + } + } + return listProto(vs...), listType(intType()), nil +} + +func encodeIntArrayFunc(a []int) (*proto3.Value, *sppb.Type, error) { + v, err := encodeArray(len(a), func(i int) interface{} { return a[i] }) + if err != nil { + return nil, nil, err + } + return v, listType(intType()), nil +} + +func encodeIntArrayReflect(a []int) (*proto3.Value, *sppb.Type, error) { + v, err := encodeArrayReflect(a) + if err != nil { + return nil, nil, err + } + return v, listType(intType()), nil +} + +func encodeArrayReflect(a interface{}) (*proto3.Value, error) { + va := reflect.ValueOf(a) + len := va.Len() + vs := make([]*proto3.Value, len) + var err error + for i := 0; i < len; i++ { + vs[i], _, err = encodeValue(va.Index(i).Interface()) + if err != nil { + return nil, err + } + } + return listProto(vs...), nil +} + +func BenchmarkDecodeGeneric(b *testing.B) { + v := stringProto("test") + t := stringType() + var g GenericColumnValue + b.ResetTimer() + for i := 0; i < b.N; i++ { + decodeValue(v, t, &g) + } +} + +func BenchmarkDecodeArray(b *testing.B) { + for _, size := range []int{1, 10, 100, 1000} { + vals := make([]*proto3.Value, size) + for i := 0; i < size; i++ { + vals[i] = dateProto(d1) + } + lv := &proto3.ListValue{Values: vals} + b.Run(strconv.Itoa(size), func(b *testing.B) { + for _, s := range []struct { + name string + decode func(*proto3.ListValue) + }{ + {"DateDirect", decodeArray_Date_direct}, + {"DateFunc", decodeArray_Date_func}, + {"DateReflect", decodeArray_Date_reflect}, + {"StringDirect", decodeArray_String_direct}, + {"StringFunc", decodeArray_String_func}, + {"StringReflect", decodeArray_String_reflect}, + } { + b.Run(s.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + s.decode(lv) + } + }) + } + }) + + } +} + +func decodeArray_Date_direct(pb *proto3.ListValue) { + a := make([]civil.Date, len(pb.Values)) + t := dateType() + for i, v := range pb.Values { + if err := decodeValue(v, t, &a[i]); err != nil { + panic(err) + } + } +} + +func decodeArray_Date_func(pb *proto3.ListValue) { + a := make([]civil.Date, len(pb.Values)) + if err := decodeArray_func(pb, "DATE", dateType(), func(i int) interface{} { return &a[i] }); err != nil { + panic(err) + } +} + +func decodeArray_Date_reflect(pb *proto3.ListValue) { + var a []civil.Date + if err := decodeArray_reflect(pb, "DATE", dateType(), &a); err != nil { + panic(err) + } +} + +func decodeArray_String_direct(pb *proto3.ListValue) { + a := make([]string, len(pb.Values)) + t := stringType() + for i, v := range pb.Values { + if err := decodeValue(v, t, &a[i]); err != nil { + panic(err) + } + } +} + +func decodeArray_String_func(pb *proto3.ListValue) { + + a := make([]string, len(pb.Values)) + if err := decodeArray_func(pb, "STRING", stringType(), func(i int) interface{} { return &a[i] }); err != nil { + panic(err) + } +} + +func decodeArray_String_reflect(pb *proto3.ListValue) { + var a []string + if err := decodeArray_reflect(pb, "STRING", stringType(), &a); err != nil { + panic(err) + } +} + +func decodeArray_func(pb *proto3.ListValue, name string, typ *sppb.Type, elptr func(int) interface{}) error { + if pb == nil { + return errNilListValue(name) + } + for i, v := range pb.Values { + if err := decodeValue(v, typ, elptr(i)); err != nil { + return errDecodeArrayElement(i, v, name, err) + } + } + return nil +} + +func decodeArray_reflect(pb *proto3.ListValue, name string, typ *sppb.Type, aptr interface{}) error { + if pb == nil { + return errNilListValue(name) + } + av := reflect.ValueOf(aptr).Elem() + av.Set(reflect.MakeSlice(av.Type(), len(pb.Values), len(pb.Values))) + for i, v := range pb.Values { + if err := decodeValue(v, typ, av.Index(i).Addr().Interface()); err != nil { + av.Set(reflect.Zero(av.Type())) // reset slice to nil + return errDecodeArrayElement(i, v, name, err) + } + } + return nil +} diff --git a/spanner/value_test.go b/spanner/value_test.go index ae8f3d235988..7fca3eb3f333 100644 --- a/spanner/value_test.go +++ b/spanner/value_test.go @@ -518,110 +518,3 @@ func TestGenericColumnValue(t *testing.T) { } } } - -func runBench(b *testing.B, size int, f func(a []int) (*proto3.Value, *sppb.Type, error)) { - a := make([]int, size) - for i := 0; i < b.N; i++ { - f(a) - } -} - -func BenchmarkEncodeIntArrayOrig1(b *testing.B) { - runBench(b, 1, encodeIntArrayOrig) -} - -func BenchmarkEncodeIntArrayOrig10(b *testing.B) { - runBench(b, 10, encodeIntArrayOrig) -} - -func BenchmarkEncodeIntArrayOrig100(b *testing.B) { - runBench(b, 100, encodeIntArrayOrig) -} - -func BenchmarkEncodeIntArrayOrig1000(b *testing.B) { - runBench(b, 1000, encodeIntArrayOrig) -} - -func BenchmarkEncodeIntArrayFunc1(b *testing.B) { - runBench(b, 1, encodeIntArrayFunc) -} - -func BenchmarkEncodeIntArrayFunc10(b *testing.B) { - runBench(b, 10, encodeIntArrayFunc) -} - -func BenchmarkEncodeIntArrayFunc100(b *testing.B) { - runBench(b, 100, encodeIntArrayFunc) -} - -func BenchmarkEncodeIntArrayFunc1000(b *testing.B) { - runBench(b, 1000, encodeIntArrayFunc) -} - -func BenchmarkEncodeIntArrayReflect1(b *testing.B) { - runBench(b, 1, encodeIntArrayReflect) -} - -func BenchmarkEncodeIntArrayReflect10(b *testing.B) { - runBench(b, 10, encodeIntArrayReflect) -} - -func BenchmarkEncodeIntArrayReflect100(b *testing.B) { - runBench(b, 100, encodeIntArrayReflect) -} - -func BenchmarkEncodeIntArrayReflect1000(b *testing.B) { - runBench(b, 1000, encodeIntArrayReflect) -} - -func encodeIntArrayOrig(a []int) (*proto3.Value, *sppb.Type, error) { - vs := make([]*proto3.Value, len(a)) - var err error - for i := range a { - vs[i], _, err = encodeValue(a[i]) - if err != nil { - return nil, nil, err - } - } - return listProto(vs...), listType(intType()), nil -} - -func encodeIntArrayFunc(a []int) (*proto3.Value, *sppb.Type, error) { - v, err := encodeArray(len(a), func(i int) interface{} { return a[i] }) - if err != nil { - return nil, nil, err - } - return v, listType(intType()), nil -} - -func encodeIntArrayReflect(a []int) (*proto3.Value, *sppb.Type, error) { - v, err := encodeArrayReflect(a) - if err != nil { - return nil, nil, err - } - return v, listType(intType()), nil -} - -func encodeArrayReflect(a interface{}) (*proto3.Value, error) { - va := reflect.ValueOf(a) - len := va.Len() - vs := make([]*proto3.Value, len) - var err error - for i := 0; i < len; i++ { - vs[i], _, err = encodeValue(va.Index(i).Interface()) - if err != nil { - return nil, err - } - } - return listProto(vs...), nil -} - -func BenchmarkDecodeGeneric(b *testing.B) { - v := stringProto("test") - t := stringType() - var g GenericColumnValue - b.ResetTimer() - for i := 0; i < b.N; i++ { - decodeValue(v, t, &g) - } -}