blob: 4f1dfd315050c80ca6d29f6ef6994495b823c351 [file] [log] [blame]
// Copyright 2021 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
//
// https://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 adapt_test
import (
"fmt"
"testing"
"time"
"cloud.google.com/go/bigquery"
"cloud.google.com/go/bigquery/storage/managedwriter/adapt"
"cloud.google.com/go/bigquery/storage/managedwriter/testdata"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/wrapperspb"
)
var benchDescriptor protoreflect.Descriptor
func BenchmarkStorageSchemaToDescriptor(b *testing.B) {
syntaxLabels := []string{"proto2", "proto3"}
for _, bm := range []struct {
name string
in bigquery.Schema
}{
{
name: "SingleField",
in: bigquery.Schema{
{Name: "field", Type: bigquery.StringFieldType},
},
},
{
name: "NestedRecord",
in: bigquery.Schema{
{Name: "field1", Type: bigquery.StringFieldType},
{Name: "field2", Type: bigquery.IntegerFieldType},
{Name: "field3", Type: bigquery.BooleanFieldType},
{
Name: "field4",
Type: bigquery.RecordFieldType,
Schema: bigquery.Schema{
{Name: "recordfield1", Type: bigquery.GeographyFieldType},
{Name: "recordfield2", Type: bigquery.TimestampFieldType},
},
},
},
},
{
name: "SimpleMessage",
in: testdata.SimpleMessageSchema,
},
{
name: "GithubArchiveSchema",
in: testdata.GithubArchiveSchema,
},
} {
for _, s := range syntaxLabels {
b.Run(fmt.Sprintf("%s-%s", bm.name, s), func(b *testing.B) {
convSchema, err := adapt.BQSchemaToStorageTableSchema(bm.in)
if err != nil {
b.Errorf("%q: schema conversion fail: %v", bm.name, err)
}
for n := 0; n < b.N; n++ {
if s == "proto3" {
benchDescriptor, err = adapt.StorageSchemaToProto3Descriptor(convSchema, "root")
} else {
benchDescriptor, err = adapt.StorageSchemaToProto2Descriptor(convSchema, "root")
}
if err != nil {
b.Errorf("failed to convert %q: %v", bm.name, err)
}
}
})
}
}
}
var staticBytes []byte
func BenchmarkStaticProtoSerialization(b *testing.B) {
for _, bm := range []struct {
name string
in bigquery.Schema
syntax string
setterF func() protoreflect.ProtoMessage
}{
{
name: "SimpleMessageProto2",
setterF: func() protoreflect.ProtoMessage {
return &testdata.SimpleMessageProto2{
Name: proto.String(fmt.Sprintf("test-%d", time.Now().UnixNano())),
Value: proto.Int64(time.Now().UnixNano()),
}
},
},
{
name: "SimpleMessageProto3",
setterF: func() protoreflect.ProtoMessage {
return &testdata.SimpleMessageProto3{
Name: fmt.Sprintf("test-%d", time.Now().UnixNano()),
Value: &wrapperspb.Int64Value{Value: time.Now().UnixNano()},
}
},
},
{
name: "GithubArchiveProto2",
setterF: func() protoreflect.ProtoMessage {
nowNano := time.Now().UnixNano()
return &testdata.GithubArchiveMessageProto2{
Type: proto.String("SomeEvent"),
Public: proto.Bool(nowNano%2 == 0),
Payload: proto.String(fmt.Sprintf("stuff %d", nowNano)),
Repo: &testdata.GithubArchiveRepoProto2{
Id: proto.Int64(nowNano),
Name: proto.String("staticname"),
Url: proto.String(fmt.Sprintf("foo.com/%d", nowNano)),
},
Actor: &testdata.GithubArchiveEntityProto2{
Id: proto.Int64(nowNano % 1000),
Login: proto.String(fmt.Sprintf("login-%d", nowNano%1000)),
GravatarId: proto.String(fmt.Sprintf("grav-%d", nowNano%1000000)),
AvatarUrl: proto.String(fmt.Sprintf("https://something.com/img/%d", nowNano%10000000)),
Url: proto.String(fmt.Sprintf("https://something.com/img/%d", nowNano%10000000)),
},
Org: &testdata.GithubArchiveEntityProto2{
Id: proto.Int64(nowNano % 1000),
Login: proto.String(fmt.Sprintf("login-%d", nowNano%1000)),
GravatarId: proto.String(fmt.Sprintf("grav-%d", nowNano%1000000)),
AvatarUrl: proto.String(fmt.Sprintf("https://something.com/img/%d", nowNano%10000000)),
Url: proto.String(fmt.Sprintf("https://something.com/img/%d", nowNano%10000000)),
},
CreatedAt: proto.Int64(nowNano),
Id: proto.String(fmt.Sprintf("id%d", nowNano)),
Other: proto.String("other"),
}
},
},
{
// Only set a single top-level field in a larger message.
name: "GithubArchiveProto2_Sparse",
setterF: func() protoreflect.ProtoMessage {
nowNano := time.Now().UnixNano()
return &testdata.GithubArchiveMessageProto2{
Id: proto.String(fmt.Sprintf("id%d", nowNano)),
}
},
},
{
name: "GithubArchiveProto3",
setterF: func() protoreflect.ProtoMessage {
nowNano := time.Now().UnixNano()
return &testdata.GithubArchiveMessageProto3{
Type: &wrapperspb.StringValue{Value: "SomeEvent"},
Public: &wrapperspb.BoolValue{Value: nowNano%2 == 0},
Payload: &wrapperspb.StringValue{Value: fmt.Sprintf("stuff %d", nowNano)},
Repo: &testdata.GithubArchiveRepoProto3{
Id: &wrapperspb.Int64Value{Value: nowNano},
Name: &wrapperspb.StringValue{Value: "staticname"},
Url: &wrapperspb.StringValue{Value: fmt.Sprintf("foo.com/%d", nowNano)},
},
Actor: &testdata.GithubArchiveEntityProto3{
Id: &wrapperspb.Int64Value{Value: nowNano % 1000},
Login: &wrapperspb.StringValue{Value: fmt.Sprintf("login-%d", nowNano%1000)},
GravatarId: &wrapperspb.StringValue{Value: fmt.Sprintf("grav-%d", nowNano%1000000)},
AvatarUrl: &wrapperspb.StringValue{Value: fmt.Sprintf("https://something.com/img/%d", nowNano%10000000)},
Url: &wrapperspb.StringValue{Value: fmt.Sprintf("https://something.com/img/%d", nowNano%10000000)},
},
Org: &testdata.GithubArchiveEntityProto3{
Id: &wrapperspb.Int64Value{Value: nowNano % 1000},
Login: &wrapperspb.StringValue{Value: fmt.Sprintf("login-%d", nowNano%1000)},
GravatarId: &wrapperspb.StringValue{Value: fmt.Sprintf("grav-%d", nowNano%1000000)},
AvatarUrl: &wrapperspb.StringValue{Value: fmt.Sprintf("https://something.com/img/%d", nowNano%10000000)},
Url: &wrapperspb.StringValue{Value: fmt.Sprintf("https://something.com/img/%d", nowNano%10000000)},
},
CreatedAt: &wrapperspb.Int64Value{Value: nowNano},
Id: &wrapperspb.StringValue{Value: fmt.Sprintf("id%d", nowNano)},
Other: &wrapperspb.StringValue{Value: "other"},
}
},
},
{
// Only set a single field in a larger message.
name: "GithubArchiveProto3_Sparse",
setterF: func() protoreflect.ProtoMessage {
nowNano := time.Now().UnixNano()
return &testdata.GithubArchiveMessageProto3{
Id: &wrapperspb.StringValue{Value: fmt.Sprintf("id%d", nowNano)},
}
},
},
} {
b.Run(bm.name, func(b *testing.B) {
var totalBytes int64
for n := 0; n < b.N; n++ {
m := bm.setterF()
out, err := proto.Marshal(m)
if err != nil {
b.Errorf("%q %q: Marshal: %v", bm.name, bm.syntax, err)
}
totalBytes = totalBytes + int64(len(out))
staticBytes = out
}
b.Logf("N=%d, avg bytes/message: %d", b.N, totalBytes/int64(b.N))
})
}
}