| // Copyright 2017 Google LLC | |
| // | |
| // 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. | |
| package spanner | |
| import ( | |
| "reflect" | |
| "strconv" | |
| "testing" | |
| "cloud.google.com/go/civil" | |
| sppb "cloud.google.com/go/spanner/apiv1/spannerpb" | |
| proto3 "github.com/golang/protobuf/ptypes/struct" | |
| ) | |
| 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 BenchmarkDecodeString(b *testing.B) { | |
| v := stringProto("test") | |
| t := stringType() | |
| var s string | |
| b.ResetTimer() | |
| for i := 0; i < b.N; i++ { | |
| decodeValue(v, t, &s) | |
| } | |
| } | |
| func BenchmarkDecodeCustomString(b *testing.B) { | |
| v := stringProto("test") | |
| t := stringType() | |
| type CustomString string | |
| var s CustomString | |
| b.ResetTimer() | |
| for i := 0; i < b.N; i++ { | |
| decodeValue(v, t, &s) | |
| } | |
| } | |
| 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", decodeArrayDateDirect}, | |
| {"DateFunc", decodeArrayDateFunc}, | |
| {"DateReflect", decodeArrayDateReflect}, | |
| {"StringDecodeStringArray", decodeStringArrayWrap}, | |
| {"StringDirect", decodeArrayStringDirect}, | |
| {"StringFunc", decodeArrayStringFunc}, | |
| {"StringReflect", decodeArrayStringReflect}, | |
| } { | |
| b.Run(s.name, func(b *testing.B) { | |
| for i := 0; i < b.N; i++ { | |
| s.decode(lv) | |
| } | |
| }) | |
| } | |
| }) | |
| } | |
| } | |
| func decodeArrayDateDirect(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 decodeArrayDateFunc(pb *proto3.ListValue) { | |
| a := make([]civil.Date, len(pb.Values)) | |
| if err := decodeArrayFunc(pb, "DATE", dateType(), func(i int) interface{} { return &a[i] }); err != nil { | |
| panic(err) | |
| } | |
| } | |
| func decodeArrayDateReflect(pb *proto3.ListValue) { | |
| var a []civil.Date | |
| if err := decodeArrayReflect(pb, "DATE", dateType(), &a); err != nil { | |
| panic(err) | |
| } | |
| } | |
| func decodeStringArrayWrap(pb *proto3.ListValue) { | |
| if _, err := decodeStringArray(pb); err != nil { | |
| panic(err) | |
| } | |
| } | |
| func decodeArrayStringDirect(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 decodeArrayStringFunc(pb *proto3.ListValue) { | |
| a := make([]string, len(pb.Values)) | |
| if err := decodeArrayFunc(pb, "STRING", stringType(), func(i int) interface{} { return &a[i] }); err != nil { | |
| panic(err) | |
| } | |
| } | |
| func decodeArrayStringReflect(pb *proto3.ListValue) { | |
| var a []string | |
| if err := decodeArrayReflect(pb, "STRING", stringType(), &a); err != nil { | |
| panic(err) | |
| } | |
| } | |
| func decodeArrayFunc(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 decodeArrayReflect(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 | |
| } |