// Copyright 2024 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto_test import ( "testing" "google.golang.org/protobuf/proto" lazyopaquepb "google.golang.org/protobuf/internal/testprotos/lazy/lazy_opaque" ) // testMessageLinked returns a test message with a few fields of various // possible types filled in that nests more messages like a linked list. func testMessageLinked(nesting int) *lazyopaquepb.Node { const ( shortVarint = 23 // encodes into 1 byte longVarint = 562949953421312 // encodes into 8 bytes ) msg := lazyopaquepb.Node_builder{ Int32: proto.Int32(shortVarint), Int64: proto.Int64(longVarint), Uint32: proto.Uint32(shortVarint), Uint64: proto.Uint64(longVarint), Sint32: proto.Int32(shortVarint), Sint64: proto.Int64(longVarint), Fixed32: proto.Uint32(shortVarint), Fixed64: proto.Uint64(longVarint), Sfixed32: proto.Int32(shortVarint), Sfixed64: proto.Int64(longVarint), Float: proto.Float32(23.42), Double: proto.Float64(23.42), Bool: proto.Bool(true), String: proto.String("hello"), Bytes: []byte("world"), }.Build() if nesting > 0 { msg.SetNested(testMessageLinked(nesting - 1)) } return msg } // A higher nesting level than 15 messages deep does not result in (relative) // performance changes. In other words, the full effect of lazy decoding is // visible with a nesting level of 15 messages deep. Lower nesting levels (like // 5 messages deep) also show significant improvement. const nesting = 15 func BenchmarkUnmarshal(b *testing.B) { encoded, err := proto.Marshal(testMessageLinked(nesting)) if err != nil { b.Fatal(err) } for _, tt := range []struct { desc string uopts proto.UnmarshalOptions }{ { desc: "lazy", uopts: proto.UnmarshalOptions{}, }, // When running the benchmark directly, print lazy vs. nolazy in the // same run. When using the benchstat tool, you can compare lazy // vs. nolazy by running only the lazy variant and disabling lazy // decoding with the -test_lazy_unmarshal command-line flag: // // benchstat \ // nolazy=<(go test -run=^$ -bench=Unmarshal/^lazy -count=6) \ // lazy=<(go test -run=^$ -bench=Unmarshal/^lazy -count=6 -test_lazy_unmarshal) { desc: "nolazy", uopts: proto.UnmarshalOptions{ NoLazyDecoding: true, }, }, } { b.Run(tt.desc, func(b *testing.B) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { out := &lazyopaquepb.Node{} if err := tt.uopts.Unmarshal(encoded, out); err != nil { b.Fatalf("can't unmarshal message: %v", err) } } }) } }