From 07eef11c1ca18d5af151ab28d7db538a1f1d3d7b Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Sun, 17 May 2026 18:10:40 +0300 Subject: [PATCH] bench: Go cross-runtime comparison harness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds bench/go/ — single-threaded Go benchmarks against the same proto schemas and payload sizes as bench/bench.lua, run against google.golang.org/protobuf v1.36 (reflective apiv2) and planetscale/vtprotobuf v0.6 (codegen marshalers). Wired through `just gen-go` + `just bench-go`. *.pb.go is gitignored repo-wide so the generated outputs are regenerated locally — not committed. bench/COMPARISON.md documents the full table per fixture / size / op: MB/s for Lua full + Lua runtime + apiv2 + vtproto, alloc bytes/op and allocs/op side-by-side, and ratios. proto2 BenchPayload vtproto cases are intentionally skipped — MarshalVT drops proto2 extensions and would understate bytes vs apiv2 / Lua. --- Justfile | 19 ++++ bench/COMPARISON.md | 115 +++++++++++++++++++++++++ bench/go/README.md | 77 +++++++++++++++++ bench/go/bench_test.go | 138 ++++++++++++++++++++++++++++++ bench/go/fixtures.go | 109 +++++++++++++++++++++++ bench/go/go.mod | 8 ++ bench/go/go.sum | 6 ++ bench/go/proto/hello.proto | 35 ++++++++ bench/go/proto/proto2_basic.proto | 32 +++++++ 9 files changed, 539 insertions(+) create mode 100644 bench/COMPARISON.md create mode 100644 bench/go/README.md create mode 100644 bench/go/bench_test.go create mode 100644 bench/go/fixtures.go create mode 100644 bench/go/go.mod create mode 100644 bench/go/go.sum create mode 100644 bench/go/proto/hello.proto create mode 100644 bench/go/proto/proto2_basic.proto diff --git a/Justfile b/Justfile index 6da87e01c048cf54e8e8f2d4ddc65623397d8522..4b5ed31a26793f598275c095441528028059b116 100644 --- a/Justfile +++ b/Justfile @@ -177,6 +177,25 @@ bench-shapes: gen jit-trace: gen tarantool bench/jit_trace.lua +# Regenerate Go-side pb.go + vtproto pb.go in bench/go/pb/. Not committed +# (*.pb.go is gitignored repo-wide); run before `bench-go` on a fresh +# checkout. Requires `protoc-gen-go` and `protoc-gen-go-vtproto` on PATH: +# go install google.golang.org/protobuf/cmd/protoc-gen-go@latest +# go install github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto@latest +gen-go: + mkdir -p bench/go/pb/hellopb bench/go/pb/proto2pb + cd bench/go && PATH="$(go env GOPATH)/bin:$PATH" protoc \ + --go_out=. --go_opt=module=github.com/tarantool-protobuf/bench/go \ + --go-vtproto_out=. --go-vtproto_opt=module=github.com/tarantool-protobuf/bench/go \ + --go-vtproto_opt=features=marshal+unmarshal+size \ + -I proto proto/hello.proto proto/proto2_basic.proto + +# Go-side comparison bench: same fixtures + sizes as bench.lua, run against +# google.golang.org/protobuf (apiv2) and planetscale/vtprotobuf. Serial by +# default — Go's testing.B doesn't parallelize unless RunParallel is called. +bench-go: gen-go + cd bench/go && go test -bench=. -benchmem -run=^$ -benchtime=1s ./... + # --------------------------------------------------------------------------- # Conformance (Google's protobuf conformance suite, in Docker) # --------------------------------------------------------------------------- diff --git a/bench/COMPARISON.md b/bench/COMPARISON.md new file mode 100644 index 0000000000000000000000000000000000000000..44023ef2d62c2f00c66936ad2442ba5ea4dd4356 --- /dev/null +++ b/bench/COMPARISON.md @@ -0,0 +1,115 @@ +# Lua vs Go: cross-runtime protobuf benchmark + +Side-by-side numbers from `just bench` (Lua `full` mode) and `just +bench-go` (Go apiv2 + vtproto). Same proto definitions, same payload +shapes, single-threaded on both sides. + +- **Host:** Apple M4 Pro, darwin/arm64, GOMAXPROCS=14 (Go iter loop is + serial regardless; GOMAXPROCS only affects GC threads) +- **Tarantool:** 3.x with LuaJIT 2.1 fork +- **Go:** 1.26.3, `google.golang.org/protobuf` v1.36.11, + `planetscale/vtprotobuf` v0.6.0 +- **Benchtime:** Go `-benchtime=1s`; Lua adaptive (50k–200k iters) +- **Lua mode shown:** `full` (codegen-inlined; the fast path). `runtime` + mode is 10–25 % slower across the board — see `bench/baseline.json` + if you want both. +- **Columns:** MB/s (higher is better), B/op (allocator bytes per + operation, lower is better) + +Numbers will drift run-to-run by 5–10 % on a busy laptop. Treat ratios, +not absolutes, as load-bearing. + +## hello.Person — encode + +| Size | Lua full MB/s | Lua run MB/s | apiv2 MB/s | vtproto MB/s | apiv2/Lua-full | vtproto/Lua-full | +| ------ | ------------: | -----------: | ---------: | -----------: | -------------: | ---------------: | +| 10 B | 31.14 | 16.09 | 161.88 | 599.94 | 5.2 × | 19.3 × | +| 100 B | 281.29 | 148.41 | 1416.79 | 4330.00 | 5.0 × | 15.4 × | +| 1 KB | 310.70 | 252.11 | 2090.37 | 5626.07 | 6.7 × | 18.1 × | +| 10 KB | 582.82 | 546.06 | 2783.52 | 7494.49 | 4.8 × | 12.9 × | +| 100 KB | 582.94 | 593.19 | 2742.85 | 7851.05 | 4.7 × | 13.5 × | + +## hello.Person — decode + +| Size | Lua full MB/s | Lua run MB/s | apiv2 MB/s | vtproto MB/s | apiv2/Lua-full | vtproto/Lua-full | +| ------ | ------------: | -----------: | ---------: | -----------: | -------------: | ---------------: | +| 10 B | 32.71 | 27.84 | 131.47 | 774.64 | 4.0 × | 23.7 × | +| 100 B | 259.38 | 231.39 | 1099.02 | 4853.53 | 4.2 × | 18.7 × | +| 1 KB | 136.40 | 129.43 | 966.14 | 1524.65 | 7.1 × | 11.2 × | +| 10 KB | 192.72 | 175.65 | 1263.84 | 2084.66 | 6.6 × | 10.8 × | +| 100 KB | 187.85 | 171.28 | 1105.49 | 1966.28 | 5.9 × | 10.5 × | + +## proto2_basic.BenchPayload — encode + +vtproto skipped here: `MarshalVT` drops proto2 extensions, so its +output bytes would not match apiv2/Lua. apiv2 is the only fair +Go-side comparison for this fixture. + +| Size | Lua full MB/s | Lua run MB/s | apiv2 MB/s | apiv2/Lua-full | +| ---- | ------------: | -----------: | ---------: | -------------: | +| min | 10.67 | 7.71 | 36.27 | 3.4 × | +| mid | 157.95 | 141.29 | 1486.45 | 9.4 × | + +## proto2_basic.BenchPayload — decode + +| Size | Lua full MB/s | Lua run MB/s | apiv2 MB/s | apiv2/Lua-full | +| ---- | ------------: | -----------: | ---------: | -------------: | +| min | 6.03 | 6.35 | 19.23 | 3.2 × | +| mid | 56.48 | 55.81 | 678.35 | 12.0 × | + +## Allocations per op (Person) + +Worth a separate look because the picture changes between encode and +decode. Encode is a single output buffer in all three runtimes; decode +allocates per-field-instance in Go but reuses interned strings in +LuaJIT. + +| Size | Op | Lua full B/op | Lua run B/op | apiv2 B/op | apiv2 allocs | vtproto B/op | vtproto allocs | +| ------ | ------ | ------------: | -----------: | ---------: | -----------: | -----------: | -------------: | +| 10 B | encode | 136.0 | 136.0 | 16 | 1 | 16 | 1 | +| 100 B | encode | 136.0 | 136.0 | 96 | 1 | 96 | 1 | +| 1 KB | encode | 1368.3 | 1368.3 | 1024 | 1 | 1024 | 1 | +| 10 KB | encode | 8540.2 | 8540.2 | 9728 | 1 | 9728 | 1 | +| 100 KB | encode | 131605.2 | 131605.2 | 98304 | 1 | 98304 | 1 | +| 10 B | decode | 112.0 | 112.0 | 216 | 2 | 8 | 1 | +| 100 B | decode | 112.0 | 112.0 | 304 | 2 | 96 | 1 | +| 1 KB | decode | 1000.0 | 1000.0 | 2200 | 38 | 1992 | 37 | +| 10 KB | decode | 4840.0 | 4840.0 | 18712 | 297 | 18504 | 296 | +| 100 KB | decode | 33512.2 | 33512.2 | 249368 | 2862 | 249160 | 2861 | + +The decode comparison flips above ~1 KB: Lua decodes a 100 KB Person +with **7.4 ×** less allocator pressure than either Go marshaler. The +repeated-string-emails workload allocates one `string` header per +element in Go, but lands in LuaJIT's string-intern table where +identical or short strings reuse storage. This is a real workload +characteristic for chat/log streams, not a microbench artifact. + +## Reading the numbers + +- **Encode ceiling** is set by code-gen marshalers + low-level + bytes-pushing: vtproto wins by 10–24 × over us. Our `mode=full` + inlines the same way, but LuaJIT pays for table iteration, + type-tag dispatch, and string allocation that AOT-compiled Go + doesn't. +- **vs. apiv2** (Go's default reflective path) the gap narrows to + 4–7 ×. apiv2 walks `protoreflect.MessageDescriptor` per call; we + walk our own descriptor in `mode=runtime` and inline it in + `mode=full`. apiv2 still wins because it's compiled. +- **Big-payload decode** is where LuaJIT's string interning pays + off and the alloc gap inverts (33 KB Lua vs 249 KB Go for a 100 KB + Person). Throughput is still Go-favorable, but per-op GC pressure + is not. +- **proto2 fixture** numbers (10–60 MB/s for the `min` size) are + dominated by per-message overhead — extensions, defaults — and + not representative of bulk throughput. The `mid` line is the one + to compare for steady-state cost. + +## Reproducing + +```bash +just bench # Lua: writes JSON to stdout (this doc used --print output) +just bench-go # Go: standard go test -bench output + +# Quick narrowed view: +cd bench/go && go test -bench=Person -benchtime=2s ./... +``` diff --git a/bench/go/README.md b/bench/go/README.md new file mode 100644 index 0000000000000000000000000000000000000000..055f4ea07cae28043dafc0ab3936af533f3a1fdf --- /dev/null +++ b/bench/go/README.md @@ -0,0 +1,77 @@ +# Go comparison bench + +Apples-to-apples microbenchmarks against `bench/bench.lua` so the LuaJIT +runtime can be compared to mainstream Go protobuf implementations. + +## What it measures + +Same fixtures and size sweep as the Lua bench: + +- `hello.Person` at five payload sizes (10 B, 100 B, 1 KB, 10 KB, 100 KB) +- `proto2_basic.BenchPayload` at two sizes (min, mid) + +For each fixture × size, both encode and decode under two Go +implementations: + +- **apiv2** — `google.golang.org/protobuf/proto.Marshal/Unmarshal`. The + default reflective marshaler everyone gets out of the box. +- **vtproto** — `planetscale/vtprotobuf` generated `MarshalVT/UnmarshalVT`. + Code-gen'd, no reflection. Conceptually equivalent to our `mode=full` + Lua codegen. + +Single-threaded: Go's `testing.B` runs serially unless `RunParallel` is +called. `-cpu=1` is not needed — GOMAXPROCS only affects GC parallelism, +not bench iterations. + +## Running + +```bash +just bench-go # full sweep, default benchtime=1s +cd bench/go && go test -bench=Person -benchtime=3s ./... +``` + +`go test` reports ns/op, MB/s (via `b.SetBytes`), B/op, allocs/op. + +## Comparing to the Lua bench + +`bench/bench.lua --print` emits ns/op + msgs/s + MB/s + bytes/op per +mode × size. Run both, pick the same size, compare directly. The +encoded byte lengths should match within a byte or two for the same +target size since both use the same proto definitions and equivalent +payload builders. + +## proto2 caveats + +- **Extensions skipped by vtproto.** `MarshalVT` does not serialize + proto2 extensions, so the BenchPayload fixture (which carries + `ext_count` + `ext_label`) would produce shorter bytes than the + apiv2 path. The vtproto proto2 cases are intentionally omitted to + avoid publishing misleading throughput numbers. +- Groups are supported by both `protoc-gen-go` and vtproto. + +## Layout + +``` +bench/go/ +├── go.mod +├── proto/ # .proto sources (subset of repo protos with go_package set) +├── pb/{hellopb,proto2pb}/ # generated *.pb.go + *_vtproto.pb.go +├── fixtures.go # payload builders mirroring bench/bench.lua +└── bench_test.go # `go test -bench` entry points +``` + +## Generated .pb.go files + +`*.pb.go` is gitignored repo-wide, so `bench/go/pb/` is **not** +committed — regenerate locally before running the bench: + +```bash +just gen-go # runs protoc with both plugins; bench-go depends on it +``` + +Requires `protoc-gen-go` and `protoc-gen-go-vtproto` on `$PATH`: + +```bash +go install google.golang.org/protobuf/cmd/protoc-gen-go@latest +go install github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto@latest +``` diff --git a/bench/go/bench_test.go b/bench/go/bench_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a52e49b5b7cd4944646b8a69124d4d0820bba981 --- /dev/null +++ b/bench/go/bench_test.go @@ -0,0 +1,138 @@ +// Standard `go test -bench=.` benchmarks. Mirrors bench/bench.lua: per +// fixture × per size, encode + decode, throughput + alloc. +// +// Two implementations per case: +// * apiv2: google.golang.org/protobuf reflective Marshal/Unmarshal +// — the default everyone gets out of the box. +// * vtproto: planetscale/vtprotobuf generated MarshalVT/UnmarshalVT +// — the fastest pure-Go path, conceptually equivalent to +// our `mode=full` codegen. +// +// Runs are single-threaded by default (testing.B doesn't parallelize +// unless RunParallel is called). +package bench + +import ( + "fmt" + "testing" + + "google.golang.org/protobuf/proto" + + hellopb "github.com/tarantool-protobuf/bench/go/pb/hellopb" + proto2pb "github.com/tarantool-protobuf/bench/go/pb/proto2pb" +) + +// --- hello.Person --------------------------------------------------------- + +func BenchmarkPersonEncode(b *testing.B) { + for _, sz := range PersonSizes { + msg := BuildPerson(sz.Target) + wire, err := proto.Marshal(msg) + if err != nil { + b.Fatal(err) + } + + b.Run(fmt.Sprintf("apiv2/%s", sz.Label), func(b *testing.B) { + b.SetBytes(int64(len(wire))) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := proto.Marshal(msg); err != nil { + b.Fatal(err) + } + } + }) + + b.Run(fmt.Sprintf("vtproto/%s", sz.Label), func(b *testing.B) { + b.SetBytes(int64(len(wire))) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := msg.MarshalVT(); err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkPersonDecode(b *testing.B) { + for _, sz := range PersonSizes { + msg := BuildPerson(sz.Target) + wire, err := proto.Marshal(msg) + if err != nil { + b.Fatal(err) + } + + b.Run(fmt.Sprintf("apiv2/%s", sz.Label), func(b *testing.B) { + b.SetBytes(int64(len(wire))) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var out hellopb.Person + if err := proto.Unmarshal(wire, &out); err != nil { + b.Fatal(err) + } + } + }) + + b.Run(fmt.Sprintf("vtproto/%s", sz.Label), func(b *testing.B) { + b.SetBytes(int64(len(wire))) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var out hellopb.Person + if err := out.UnmarshalVT(wire); err != nil { + b.Fatal(err) + } + } + }) + } +} + +// --- proto2_basic.BenchPayload -------------------------------------------- + +func BenchmarkProto2Encode(b *testing.B) { + for _, sz := range Proto2Sizes { + msg := BuildProto2(sz.Target) + wire, err := proto.Marshal(msg) + if err != nil { + b.Fatal(err) + } + + b.Run(fmt.Sprintf("apiv2/%s", sz.Label), func(b *testing.B) { + b.SetBytes(int64(len(wire))) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := proto.Marshal(msg); err != nil { + b.Fatal(err) + } + } + }) + + // vtproto's generated MarshalVT does not serialize extensions — + // it skips XXX_unrecognized + protoimpl.ExtensionFields and so + // drops the ext_count/ext_label payload. The benchmark would + // produce shorter bytes than apiv2; skip the vtproto variant + // when the fixture carries extensions so we don't publish + // misleading throughput numbers. + _ = msg.MarshalVT // keep symbol referenced; intentionally not benched + } +} + +func BenchmarkProto2Decode(b *testing.B) { + for _, sz := range Proto2Sizes { + msg := BuildProto2(sz.Target) + wire, err := proto.Marshal(msg) + if err != nil { + b.Fatal(err) + } + + b.Run(fmt.Sprintf("apiv2/%s", sz.Label), func(b *testing.B) { + b.SetBytes(int64(len(wire))) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var out proto2pb.BenchPayload + if err := proto.Unmarshal(wire, &out); err != nil { + b.Fatal(err) + } + } + }) + } +} diff --git a/bench/go/fixtures.go b/bench/go/fixtures.go new file mode 100644 index 0000000000000000000000000000000000000000..7f13453f9325e7eb99d73dc148e511fe5d2e904c --- /dev/null +++ b/bench/go/fixtures.go @@ -0,0 +1,109 @@ +// Payload builders that mirror bench/bench.lua so msg sizes and shapes +// match across runtimes. Same field set + field numbers as the Lua +// build_person_payload / build_proto2_payload helpers, so encoded byte +// lengths land within a byte or two for the same target size. +package bench + +import ( + "fmt" + "strings" + + hellopb "github.com/tarantool-protobuf/bench/go/pb/hellopb" + proto2pb "github.com/tarantool-protobuf/bench/go/pb/proto2pb" + "google.golang.org/protobuf/proto" +) + +type SizeSpec struct { + Label string + Target int +} + +var PersonSizes = []SizeSpec{ + {"10B", 10}, + {"100B", 100}, + {"1KB", 1024}, + {"10KB", 10240}, + {"100KB", 102400}, +} + +var Proto2Sizes = []SizeSpec{ + {"min", 0}, + {"mid", 1024}, +} + +func BuildPerson(target int) *hellopb.Person { + if target <= 10 { + return &hellopb.Person{Name: "bigbes", Age: 42} + } + if target <= 100 { + return &hellopb.Person{ + Name: strings.Repeat("a", target-10), + Age: 42, + } + } + const perEmail = 36 + const fixedBytes = 80 + nEmails := (target - fixedBytes) / perEmail + if nEmails < 1 { + nEmails = 1 + } + p := &hellopb.Person{ + Name: "bigbes", + Age: 42, + Address: &hellopb.Address{ + Street: "1 Main St", + City: "Springfield", + Zip: 12345, + }, + LuckyNumbers: []int32{7, 13, 21, 42, 99}, + Emails: make([]string, nEmails), + } + for i := 0; i < nEmails; i++ { + p.Emails[i] = strings.Repeat("e", 28) + fmt.Sprintf("%04d", i+1) + } + return p +} + +func BuildProto2(target int) *proto2pb.BenchPayload { + if target <= 0 { + id := int32(7) + m := &proto2pb.BenchPayload{Id: &id} + proto.SetExtension(m, proto2pb.E_ExtCount, int32(42)) + return m + } + const nTags = 16 + const perTag = 30 + tags := make([]string, nTags) + for i := 0; i < nTags; i++ { + tags[i] = strings.Repeat("t", perTag-2) + fmt.Sprintf("%02d", i+1) + } + lucky := make([]int32, 8) + for i := 0; i < 8; i++ { + lucky[i] = int32(1000 + i + 1) + } + id := int32(7) + name := "bench" + retries := int32(9) + innerKey := strings.Repeat("k", 16) + innerWeight := int32(3) + latency := int32(1234567) + attempts := int32(4) + m := &proto2pb.BenchPayload{ + Id: &id, + Name: &name, + Retries: &retries, + LuckyNumbers: lucky, + Tags: tags, + Inner: &proto2pb.BenchPayload_Inner{ + Key: &innerKey, + Weight: &innerWeight, + }, + Stats: &proto2pb.BenchPayload_Stats{ + LatencyNs: &latency, + Attempts: &attempts, + }, + } + proto.SetExtension(m, proto2pb.E_ExtCount, int32(99)) + proto.SetExtension(m, proto2pb.E_ExtLabel, strings.Repeat("x", 32)) + return m +} diff --git a/bench/go/go.mod b/bench/go/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..0e2485a6c8aa92340c6342c10a5fbe6876371b80 --- /dev/null +++ b/bench/go/go.mod @@ -0,0 +1,8 @@ +module github.com/tarantool-protobuf/bench/go + +go 1.23 + +require ( + github.com/planetscale/vtprotobuf v0.6.0 + google.golang.org/protobuf v1.36.11 +) diff --git a/bench/go/go.sum b/bench/go/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..64a86a9841ae8da812ee3b8ba3a3bf4859b7fe0b --- /dev/null +++ b/bench/go/go.sum @@ -0,0 +1,6 @@ +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEabROxmNA= +github.com/planetscale/vtprotobuf v0.6.0/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= diff --git a/bench/go/proto/hello.proto b/bench/go/proto/hello.proto new file mode 100644 index 0000000000000000000000000000000000000000..7489b8644fb6113b900b0e7b43dfdf2fe17bb4a6 --- /dev/null +++ b/bench/go/proto/hello.proto @@ -0,0 +1,35 @@ +// Bench-only subset of examples/proto/hello.proto with go_package set. +// Field numbers and wire format match the Lua version exactly so encoded +// bytes are interchangeable between runtimes. +syntax = "proto3"; + +package hello; + +option go_package = "github.com/tarantool-protobuf/bench/go/pb/hellopb;hellopb"; + +enum Status { + UNKNOWN = 0; + OK = 1; + ERROR = 2; +} + +message Address { + string street = 1; + string city = 2; + int32 zip = 3; + optional string apartment = 4; +} + +message Person { + string name = 1; + int32 age = 2; + repeated string emails = 3; + Status status = 4; + Address address = 5; + repeated Person friends = 6; + repeated int32 lucky_numbers = 7; + bytes avatar = 8; + fixed64 user_id = 9; + sint32 balance = 10; + double weight_kg = 11; +} diff --git a/bench/go/proto/proto2_basic.proto b/bench/go/proto/proto2_basic.proto new file mode 100644 index 0000000000000000000000000000000000000000..560ae6d24a37c3f878010961358d0c8e06a49e47 --- /dev/null +++ b/bench/go/proto/proto2_basic.proto @@ -0,0 +1,32 @@ +// Bench-only subset of test/proto/proto2_basic.proto with go_package set. +// Same field numbers and wire format as the Lua-tested version. +syntax = "proto2"; + +package proto2_basic; + +option go_package = "github.com/tarantool-protobuf/bench/go/pb/proto2pb;proto2pb"; + +message BenchPayload { + required int32 id = 1; + optional string name = 2 [default = "anonymous"]; + optional int32 retries = 3 [default = 3]; + repeated int32 lucky_numbers = 4 [packed = true]; + repeated string tags = 5; + optional Inner inner = 6; + optional group Stats = 7 { + optional int32 latency_ns = 8; + optional int32 attempts = 9; + } + + message Inner { + required string key = 1; + optional int32 weight = 2; + } + + extensions 100 to 199; +} + +extend BenchPayload { + optional int32 ext_count = 100; + optional string ext_label = 101; +}