mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-01 03:14:16 +00:00
e8e8875f94
Comparing -tags=protoreflect to fast-path: name old time/op new time/op delta pkg:google.golang.org/protobuf/internal/benchmarks goos:linux goarch:amd64 /Clone/google_message1_proto2-12 1.70µs ± 1% 0.30µs ± 1% -82.64% (p=0.001 n=7+7) /Clone/google_message1_proto3-12 1.01µs ± 1% 0.19µs ± 1% -80.77% (p=0.000 n=7+8) /Clone/google_message2-12 818µs ± 8% 141µs ± 6% -82.78% (p=0.000 n=8+8) pkg:google.golang.org/protobuf/internal/benchmarks/micro goos:linux goarch:amd64 EmptyMessage/Clone-12 51.1ns ± 1% 39.3ns ± 3% -23.03% (p=0.000 n=7+8) RepeatedInt32/Clone-12 24.5µs ± 1% 1.1µs ± 3% -95.64% (p=0.000 n=8+8) Required/Clone-12 978ns ± 1% 132ns ± 2% -86.46% (p=0.000 n=8+8) name old alloc/op new alloc/op delta pkg:google.golang.org/protobuf/internal/benchmarks goos:linux goarch:amd64 /Clone/google_message1_proto2-12 1.08kB ± 0% 0.74kB ± 0% -31.85% (p=0.000 n=8+8) /Clone/google_message1_proto3-12 872B ± 0% 544B ± 0% -37.61% (p=0.000 n=8+8) /Clone/google_message2-12 602kB ± 0% 411kB ± 0% -31.65% (p=0.000 n=8+8) pkg:google.golang.org/protobuf/internal/benchmarks/micro goos:linux goarch:amd64 EmptyMessage/Clone-12 96.0B ± 0% 64.0B ± 0% -33.33% (p=0.000 n=8+8) RepeatedInt32/Clone-12 25.4kB ± 0% 3.2kB ± 0% -87.33% (p=0.000 n=8+8) Required/Clone-12 416B ± 0% 256B ± 0% -38.46% (p=0.000 n=8+8) name old allocs/op new allocs/op delta pkg:google.golang.org/protobuf/internal/benchmarks goos:linux goarch:amd64 /Clone/google_message1_proto2-12 52.0 ± 0% 21.0 ± 0% -59.62% (p=0.000 n=8+8) /Clone/google_message1_proto3-12 33.0 ± 0% 3.0 ± 0% -90.91% (p=0.000 n=8+8) /Clone/google_message2-12 22.3k ± 0% 7.5k ± 0% -66.41% (p=0.000 n=8+8) pkg:google.golang.org/protobuf/internal/benchmarks/micro goos:linux goarch:amd64 EmptyMessage/Clone-12 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=8+8) RepeatedInt32/Clone-12 1.51k ± 0% 0.00k ± 0% -99.80% (p=0.000 n=8+8) Required/Clone-12 51.0 ± 0% 18.0 ± 0% -64.71% (p=0.000 n=8+8) Change-Id: Ife9018097c34cb025dc9c4fdd9a61b2f947853c6 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/219147 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
217 lines
5.9 KiB
Go
217 lines
5.9 KiB
Go
// Copyright 2019 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 bench_test
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"google.golang.org/protobuf/encoding/protojson"
|
|
"google.golang.org/protobuf/encoding/prototext"
|
|
"google.golang.org/protobuf/proto"
|
|
pref "google.golang.org/protobuf/reflect/protoreflect"
|
|
preg "google.golang.org/protobuf/reflect/protoregistry"
|
|
|
|
benchpb "google.golang.org/protobuf/internal/testprotos/benchmarks"
|
|
_ "google.golang.org/protobuf/internal/testprotos/benchmarks/datasets/google_message1/proto2"
|
|
_ "google.golang.org/protobuf/internal/testprotos/benchmarks/datasets/google_message1/proto3"
|
|
_ "google.golang.org/protobuf/internal/testprotos/benchmarks/datasets/google_message2"
|
|
_ "google.golang.org/protobuf/internal/testprotos/benchmarks/datasets/google_message3"
|
|
_ "google.golang.org/protobuf/internal/testprotos/benchmarks/datasets/google_message4"
|
|
)
|
|
|
|
func BenchmarkWire(b *testing.B) {
|
|
bench(b, "Unmarshal", func(ds dataset, pb *testing.PB) {
|
|
for pb.Next() {
|
|
for _, p := range ds.wire {
|
|
m := ds.messageType.New().Interface()
|
|
if err := proto.Unmarshal(p, m); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
bench(b, "Marshal", func(ds dataset, pb *testing.PB) {
|
|
for pb.Next() {
|
|
for _, m := range ds.messages {
|
|
if _, err := proto.Marshal(m); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
bench(b, "Size", func(ds dataset, pb *testing.PB) {
|
|
for pb.Next() {
|
|
for _, m := range ds.messages {
|
|
proto.Size(m)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func BenchmarkText(b *testing.B) {
|
|
bench(b, "Unmarshal", func(ds dataset, pb *testing.PB) {
|
|
for pb.Next() {
|
|
for _, p := range ds.text {
|
|
m := ds.messageType.New().Interface()
|
|
if err := prototext.Unmarshal(p, m); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
bench(b, "Marshal", func(ds dataset, pb *testing.PB) {
|
|
for pb.Next() {
|
|
for _, m := range ds.messages {
|
|
if _, err := prototext.Marshal(m); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func BenchmarkJSON(b *testing.B) {
|
|
bench(b, "Unmarshal", func(ds dataset, pb *testing.PB) {
|
|
for pb.Next() {
|
|
for _, p := range ds.json {
|
|
m := ds.messageType.New().Interface()
|
|
if err := protojson.Unmarshal(p, m); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
bench(b, "Marshal", func(ds dataset, pb *testing.PB) {
|
|
for pb.Next() {
|
|
for _, m := range ds.messages {
|
|
if _, err := protojson.Marshal(m); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func Benchmark(b *testing.B) {
|
|
bench(b, "Clone", func(ds dataset, pb *testing.PB) {
|
|
for pb.Next() {
|
|
for _, src := range ds.messages {
|
|
proto.Clone(src)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func bench(b *testing.B, name string, f func(dataset, *testing.PB)) {
|
|
b.Helper()
|
|
b.Run(name, func(b *testing.B) {
|
|
for _, ds := range datasets {
|
|
b.Run(ds.name, func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
f(ds, pb)
|
|
})
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
type dataset struct {
|
|
name string
|
|
messageType pref.MessageType
|
|
messages []proto.Message
|
|
wire [][]byte
|
|
text [][]byte
|
|
json [][]byte
|
|
}
|
|
|
|
var datasets []dataset
|
|
|
|
func TestMain(m *testing.M) {
|
|
// Load benchmark data early, to avoid including this step in -cpuprofile/-memprofile.
|
|
//
|
|
// For the larger benchmark datasets (not downloaded by default), preparing
|
|
// this data is quite expensive. In addition, keeping the unmarshaled messages
|
|
// in memory makes GC scans a substantial fraction of runtime CPU cost.
|
|
//
|
|
// It would be nice to avoid loading the data we aren't going to use. Unfortunately,
|
|
// there isn't any simple way to tell what benchmarks are going to run; we can examine
|
|
// the -test.bench flag, but parsing it is quite complicated.
|
|
flag.Parse()
|
|
if v := flag.Lookup("test.bench").Value.(flag.Getter).Get(); v == "" {
|
|
// Don't bother loading data if we aren't going to run any benchmarks.
|
|
// Avoids slowing down go test ./...
|
|
return
|
|
}
|
|
if v := flag.Lookup("test.timeout").Value.(flag.Getter).Get().(time.Duration); v != 0 && v <= 10*time.Minute {
|
|
// The default test timeout of 10m is too short if running all the benchmarks.
|
|
// It's quite frustrating to discover this 10m through a benchmark run, so
|
|
// catch the condition.
|
|
//
|
|
// The -timeout and -test.timeout flags are handled by the go command, which
|
|
// forwards them along to the test binary, so we can't just set the default
|
|
// to something reasonable; the go command will override it with its default.
|
|
// We also can't ignore the timeout, because the go command kills a test which
|
|
// runs more than a minute past its deadline.
|
|
fmt.Fprintf(os.Stderr, "Test timeout of %v is probably too short; set -test.timeout=0.\n", v)
|
|
os.Exit(1)
|
|
}
|
|
out, err := exec.Command("git", "rev-parse", "--show-toplevel").CombinedOutput()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
repoRoot := strings.TrimSpace(string(out))
|
|
dataDir := filepath.Join(repoRoot, ".cache", "benchdata")
|
|
filepath.Walk(dataDir, func(path string, _ os.FileInfo, _ error) error {
|
|
if filepath.Ext(path) != ".pb" {
|
|
return nil
|
|
}
|
|
raw, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
dspb := &benchpb.BenchmarkDataset{}
|
|
if err := proto.Unmarshal(raw, dspb); err != nil {
|
|
panic(err)
|
|
}
|
|
mt, err := preg.GlobalTypes.FindMessageByName(pref.FullName(dspb.MessageName))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
ds := dataset{
|
|
name: dspb.Name,
|
|
messageType: mt,
|
|
wire: dspb.Payload,
|
|
}
|
|
for _, payload := range dspb.Payload {
|
|
m := mt.New().Interface()
|
|
if err := proto.Unmarshal(payload, m); err != nil {
|
|
panic(err)
|
|
}
|
|
ds.messages = append(ds.messages, m)
|
|
b, err := prototext.Marshal(m)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
ds.text = append(ds.text, b)
|
|
b, err = protojson.Marshal(m)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
ds.json = append(ds.json, b)
|
|
}
|
|
datasets = append(datasets, ds)
|
|
return nil
|
|
})
|
|
os.Exit(m.Run())
|
|
}
|