grpc-go/rpc_util_test.go

289 строки
8.2 KiB
Go
Исходник Обычный вид История

2015-02-06 04:14:05 +03:00
/*
*
2017-06-08 15:42:19 +03:00
* Copyright 2014 gRPC authors.
2015-02-06 04:14:05 +03:00
*
2017-06-08 15:42:19 +03:00
* 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
2015-02-06 04:14:05 +03:00
*
2017-06-08 15:42:19 +03:00
* http://www.apache.org/licenses/LICENSE-2.0
2015-02-06 04:14:05 +03:00
*
2017-06-08 15:42:19 +03:00
* 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.
2015-02-06 04:14:05 +03:00
*
*/
package grpc
2015-02-06 04:14:05 +03:00
import (
"bytes"
"compress/gzip"
2015-02-06 04:14:05 +03:00
"io"
"math"
2015-02-06 04:14:05 +03:00
"reflect"
"testing"
"github.com/golang/protobuf/proto"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/encoding"
protoenc "google.golang.org/grpc/encoding/proto"
"google.golang.org/grpc/internal/transport"
"google.golang.org/grpc/status"
Benchmark encode and cut encoding inefficiency. This commit introduces the first microbenchmark for grpc, wherein `encode` is benchmarked according to message size. A conclusion of the benchmark is that the removal of type switching found in `binary.Write`, which is used in `encode` produces the following encoding time and memory allocation footprint: ``` $ # Return to previous commit but benchmark. $ go test ./... -test.bench="Benchmark*" > /tmp/before $ # Return to working copy. $ go test ./... -test.bench="Benchmark*" > /tmp/after $ benchcmp /tmp/before /tmp/after benchmark old ns/op new ns/op delta BenchmarkEncode1B 1282 936 -26.99% BenchmarkEncode1KiB 4865 4184 -14.00% BenchmarkEncode8KiB 22686 21560 -4.96% BenchmarkEncode64KiB 134451 116762 -13.16% BenchmarkEncode512KiB 514044 361224 -29.73% BenchmarkEncode1MiB 767096 636725 -17.00% benchmark old MB/s new MB/s speedup BenchmarkEncode1B 6.24 8.55 1.37x BenchmarkEncode1KiB 212.11 246.63 1.16x BenchmarkEncode8KiB 361.46 380.33 1.05x BenchmarkEncode64KiB 487.50 561.35 1.15x BenchmarkEncode512KiB 1019.94 1451.45 1.42x BenchmarkEncode1MiB 1366.95 1646.84 1.20x benchmark old allocs new allocs delta BenchmarkEncode1B 6 3 -50.00% BenchmarkEncode1KiB 8 5 -37.50% BenchmarkEncode8KiB 8 5 -37.50% BenchmarkEncode64KiB 8 5 -37.50% BenchmarkEncode512KiB 8 5 -37.50% BenchmarkEncode1MiB 8 5 -37.50% benchmark old bytes new bytes delta BenchmarkEncode1B 384 328 -14.58% BenchmarkEncode1KiB 2816 2760 -1.99% BenchmarkEncode8KiB 17283 17227 -0.32% BenchmarkEncode64KiB 147856 147802 -0.04% BenchmarkEncode512KiB 1065344 1065288 -0.01% BenchmarkEncode1MiB 2113920 2113864 -0.00% ``` ..., which is apropos of the comment in [encoding/binary] (http://golang.org/pkg/encoding/binary), wherein ... > This package favors simplicity over efficiency. ... is stated. If `encode` is deemed to need further memory efficiencies, a mechanism whereby a `proto.Buffer` is retained may be warranted, which is why the original TODO remains. The proposed improvement in this change is simple and low-hanging. I did not want to introduce yet-another protocol buffer message for tests, but the ones under ... > interop/grpc_testing/test.proto > test/grpc_testing/test.proto ... have a fundamental dependency on `grpc` package due to their generated stubs, which produces a cycle in the imports if the benchmark were to attempt to import them for profiling. The newly created ... > test/grpc_message/test.proto ... protocol buffer package has no generated RPC service stubs, which means it can be imported into the `grpc` package root without cycle.
2015-02-28 20:46:39 +03:00
perfpb "google.golang.org/grpc/test/codec_perf"
2015-02-06 04:14:05 +03:00
)
type fullReader struct {
reader io.Reader
}
func (f fullReader) Read(p []byte) (int, error) {
return io.ReadFull(f.reader, p)
}
2017-05-18 00:16:19 +03:00
var _ CallOption = EmptyCallOption{} // ensure EmptyCallOption implements the interface
func TestSimpleParsing(t *testing.T) {
bigMsg := bytes.Repeat([]byte{'x'}, 1<<24)
for _, test := range []struct {
// input
p []byte
// outputs
err error
b []byte
pt payloadFormat
}{
{nil, io.EOF, nil, compressionNone},
{[]byte{0, 0, 0, 0, 0}, nil, nil, compressionNone},
{[]byte{0, 0, 0, 0, 1, 'a'}, nil, []byte{'a'}, compressionNone},
{[]byte{1, 0}, io.ErrUnexpectedEOF, nil, compressionNone},
{[]byte{0, 0, 0, 0, 10, 'a'}, io.ErrUnexpectedEOF, nil, compressionNone},
// Check that messages with length >= 2^24 are parsed.
{append([]byte{0, 1, 0, 0, 0}, bigMsg...), nil, bigMsg, compressionNone},
} {
buf := fullReader{bytes.NewReader(test.p)}
parser := &parser{r: buf}
pt, b, err := parser.recvMsg(math.MaxInt32)
if err != test.err || !bytes.Equal(b, test.b) || pt != test.pt {
t.Fatalf("parser{%v}.recvMsg(_) = %v, %v, %v\nwant %v, %v, %v", test.p, pt, b, err, test.pt, test.b, test.err)
}
}
}
func TestMultipleParsing(t *testing.T) {
// Set a byte stream consists of 3 messages with their headers.
p := []byte{0, 0, 0, 0, 1, 'a', 0, 0, 0, 0, 2, 'b', 'c', 0, 0, 0, 0, 1, 'd'}
b := fullReader{bytes.NewReader(p)}
parser := &parser{r: b}
wantRecvs := []struct {
pt payloadFormat
data []byte
}{
{compressionNone, []byte("a")},
{compressionNone, []byte("bc")},
{compressionNone, []byte("d")},
}
for i, want := range wantRecvs {
pt, data, err := parser.recvMsg(math.MaxInt32)
if err != nil || pt != want.pt || !reflect.DeepEqual(data, want.data) {
t.Fatalf("after %d calls, parser{%v}.recvMsg(_) = %v, %v, %v\nwant %v, %v, <nil>",
i, p, pt, data, err, want.pt, want.data)
}
}
pt, data, err := parser.recvMsg(math.MaxInt32)
if err != io.EOF {
t.Fatalf("after %d recvMsgs calls, parser{%v}.recvMsg(_) = %v, %v, %v\nwant _, _, %v",
len(wantRecvs), p, pt, data, err, io.EOF)
}
}
2015-02-06 04:14:05 +03:00
func TestEncode(t *testing.T) {
for _, test := range []struct {
// input
msg proto.Message
// outputs
hdr []byte
data []byte
err error
2015-02-06 04:14:05 +03:00
}{
{nil, []byte{0, 0, 0, 0, 0}, []byte{}, nil},
2015-02-06 04:14:05 +03:00
} {
data, err := encode(encoding.GetCodec(protoenc.Name), test.msg)
if err != test.err || !bytes.Equal(data, test.data) {
t.Errorf("encode(_, %v) = %v, %v; want %v, %v", test.msg, data, err, test.data, test.err)
continue
}
if hdr, _ := msgHeader(data, nil); !bytes.Equal(hdr, test.hdr) {
t.Errorf("msgHeader(%v, false) = %v; want %v", data, hdr, test.hdr)
2016-01-23 05:21:41 +03:00
}
}
}
func TestCompress(t *testing.T) {
bestCompressor, err := NewGZIPCompressorWithLevel(gzip.BestCompression)
if err != nil {
t.Fatalf("Could not initialize gzip compressor with best compression.")
}
bestSpeedCompressor, err := NewGZIPCompressorWithLevel(gzip.BestSpeed)
if err != nil {
t.Fatalf("Could not initialize gzip compressor with best speed compression.")
}
defaultCompressor, err := NewGZIPCompressorWithLevel(gzip.BestSpeed)
if err != nil {
t.Fatalf("Could not initialize gzip compressor with default compression.")
}
level5, err := NewGZIPCompressorWithLevel(5)
if err != nil {
t.Fatalf("Could not initialize gzip compressor with level 5 compression.")
}
2016-01-23 05:21:41 +03:00
for _, test := range []struct {
// input
data []byte
cp Compressor
dc Decompressor
// outputs
err error
}{
Use pooled gzip.{Writer,Reader} in gzip{Compressor,Decompressor} (#1217) This change saves a lot of memory by reusing the underlying gzip.{Writer,Reader}, which allocates up to 1.4mb at every instanciation according to [1]. This was fixed by adding a Reset method by to the object at [2]. The amount of memory (and GC time) saved is pretty high, as reported by pprof: flat flat% sum% cum cum% 28.33GB 85.70% 85.70% 32.74GB 99.05% compress/flate.NewWriter flat flat% sum% cum cum% 19.39MB 16.74% 16.74% 22.07MB 19.05% compress/flate.NewWriter And the benchmarks: benchmark old ns/op new ns/op delta BenchmarkGZIPCompressor1B-4 215170 22291 -89.64% BenchmarkGZIPCompressor1KiB-4 225971 27213 -87.96% BenchmarkGZIPCompressor8KiB-4 246696 54785 -77.79% BenchmarkGZIPCompressor64KiB-4 444851 286924 -35.50% BenchmarkGZIPCompressor512KiB-4 2279043 2115863 -7.16% BenchmarkGZIPCompressor1MiB-4 4412989 4258635 -3.50% benchmark old allocs new allocs delta BenchmarkGZIPCompressor1B-4 17 0 -100.00% BenchmarkGZIPCompressor1KiB-4 17 0 -100.00% BenchmarkGZIPCompressor8KiB-4 17 0 -100.00% BenchmarkGZIPCompressor64KiB-4 17 0 -100.00% BenchmarkGZIPCompressor512KiB-4 17 0 -100.00% BenchmarkGZIPCompressor1MiB-4 17 0 -100.00% benchmark old bytes new bytes delta BenchmarkGZIPCompressor1B-4 813872 8 -100.00% BenchmarkGZIPCompressor1KiB-4 813872 16 -100.00% BenchmarkGZIPCompressor8KiB-4 813875 27 -100.00% BenchmarkGZIPCompressor64KiB-4 813918 190 -99.98% BenchmarkGZIPCompressor512KiB-4 814928 1871 -99.77% BenchmarkGZIPCompressor1MiB-4 820889 9735 -98.81% [1] https://github.com/golang/go/issues/6138 [2] https://github.com/golang/go/commit/db12f9d4e406dcab81b476e955c8e119112522fa Signed-off-by: Steeve Morin <steeve.morin@gmail.com>
2017-05-11 02:40:25 +03:00
{make([]byte, 1024), NewGZIPCompressor(), NewGZIPDecompressor(), nil},
{make([]byte, 1024), bestCompressor, NewGZIPDecompressor(), nil},
{make([]byte, 1024), bestSpeedCompressor, NewGZIPDecompressor(), nil},
{make([]byte, 1024), defaultCompressor, NewGZIPDecompressor(), nil},
{make([]byte, 1024), level5, NewGZIPDecompressor(), nil},
2016-01-23 05:21:41 +03:00
} {
b := new(bytes.Buffer)
if err := test.cp.Do(b, test.data); err != test.err {
t.Fatalf("Compressor.Do(_, %v) = %v, want %v", test.data, err, test.err)
}
if b.Len() >= len(test.data) {
t.Fatalf("The compressor fails to compress data.")
}
if p, err := test.dc.Do(b); err != nil || !bytes.Equal(test.data, p) {
t.Fatalf("Decompressor.Do(%v) = %v, %v, want %v, <nil>", b, p, err, test.data)
2015-02-06 04:14:05 +03:00
}
}
}
func TestToRPCErr(t *testing.T) {
for _, test := range []struct {
// input
errIn error
// outputs
errOut error
2015-02-06 04:14:05 +03:00
}{
{transport.ErrConnClosing, status.Error(codes.Unavailable, transport.ErrConnClosing.Desc)},
{io.ErrUnexpectedEOF, status.Error(codes.Internal, io.ErrUnexpectedEOF.Error())},
2015-02-06 04:14:05 +03:00
} {
err := toRPCErr(test.errIn)
if _, ok := status.FromError(err); !ok {
t.Fatalf("toRPCErr{%v} returned type %T, want %T", test.errIn, err, status.Error(codes.Unknown, ""))
2016-05-25 23:56:09 +03:00
}
if !reflect.DeepEqual(err, test.errOut) {
2015-02-06 04:14:05 +03:00
t.Fatalf("toRPCErr{%v} = %v \nwant %v", test.errIn, err, test.errOut)
}
}
}
func TestParseDialTarget(t *testing.T) {
for _, test := range []struct {
target, wantNet, wantAddr string
}{
{"unix:etcd:0", "unix", "etcd:0"},
{"unix:///tmp/unix-3", "unix", "/tmp/unix-3"},
{"unix://domain", "unix", "domain"},
{"unix://etcd:0", "unix", "etcd:0"},
{"unix:///etcd:0", "unix", "/etcd:0"},
{"passthrough://unix://domain", "tcp", "passthrough://unix://domain"},
{"https://google.com:443", "tcp", "https://google.com:443"},
{"dns:///google.com", "tcp", "dns:///google.com"},
{"/unix/socket/address", "tcp", "/unix/socket/address"},
} {
gotNet, gotAddr := parseDialTarget(test.target)
if gotNet != test.wantNet || gotAddr != test.wantAddr {
t.Errorf("parseDialTarget(%q) = %s, %s want %s, %s", test.target, gotNet, gotAddr, test.wantNet, test.wantAddr)
}
}
}
Benchmark encode and cut encoding inefficiency. This commit introduces the first microbenchmark for grpc, wherein `encode` is benchmarked according to message size. A conclusion of the benchmark is that the removal of type switching found in `binary.Write`, which is used in `encode` produces the following encoding time and memory allocation footprint: ``` $ # Return to previous commit but benchmark. $ go test ./... -test.bench="Benchmark*" > /tmp/before $ # Return to working copy. $ go test ./... -test.bench="Benchmark*" > /tmp/after $ benchcmp /tmp/before /tmp/after benchmark old ns/op new ns/op delta BenchmarkEncode1B 1282 936 -26.99% BenchmarkEncode1KiB 4865 4184 -14.00% BenchmarkEncode8KiB 22686 21560 -4.96% BenchmarkEncode64KiB 134451 116762 -13.16% BenchmarkEncode512KiB 514044 361224 -29.73% BenchmarkEncode1MiB 767096 636725 -17.00% benchmark old MB/s new MB/s speedup BenchmarkEncode1B 6.24 8.55 1.37x BenchmarkEncode1KiB 212.11 246.63 1.16x BenchmarkEncode8KiB 361.46 380.33 1.05x BenchmarkEncode64KiB 487.50 561.35 1.15x BenchmarkEncode512KiB 1019.94 1451.45 1.42x BenchmarkEncode1MiB 1366.95 1646.84 1.20x benchmark old allocs new allocs delta BenchmarkEncode1B 6 3 -50.00% BenchmarkEncode1KiB 8 5 -37.50% BenchmarkEncode8KiB 8 5 -37.50% BenchmarkEncode64KiB 8 5 -37.50% BenchmarkEncode512KiB 8 5 -37.50% BenchmarkEncode1MiB 8 5 -37.50% benchmark old bytes new bytes delta BenchmarkEncode1B 384 328 -14.58% BenchmarkEncode1KiB 2816 2760 -1.99% BenchmarkEncode8KiB 17283 17227 -0.32% BenchmarkEncode64KiB 147856 147802 -0.04% BenchmarkEncode512KiB 1065344 1065288 -0.01% BenchmarkEncode1MiB 2113920 2113864 -0.00% ``` ..., which is apropos of the comment in [encoding/binary] (http://golang.org/pkg/encoding/binary), wherein ... > This package favors simplicity over efficiency. ... is stated. If `encode` is deemed to need further memory efficiencies, a mechanism whereby a `proto.Buffer` is retained may be warranted, which is why the original TODO remains. The proposed improvement in this change is simple and low-hanging. I did not want to introduce yet-another protocol buffer message for tests, but the ones under ... > interop/grpc_testing/test.proto > test/grpc_testing/test.proto ... have a fundamental dependency on `grpc` package due to their generated stubs, which produces a cycle in the imports if the benchmark were to attempt to import them for profiling. The newly created ... > test/grpc_message/test.proto ... protocol buffer package has no generated RPC service stubs, which means it can be imported into the `grpc` package root without cycle.
2015-02-28 20:46:39 +03:00
// bmEncode benchmarks encoding a Protocol Buffer message containing mSize
// bytes.
func bmEncode(b *testing.B, mSize int) {
cdc := encoding.GetCodec(protoenc.Name)
Benchmark encode and cut encoding inefficiency. This commit introduces the first microbenchmark for grpc, wherein `encode` is benchmarked according to message size. A conclusion of the benchmark is that the removal of type switching found in `binary.Write`, which is used in `encode` produces the following encoding time and memory allocation footprint: ``` $ # Return to previous commit but benchmark. $ go test ./... -test.bench="Benchmark*" > /tmp/before $ # Return to working copy. $ go test ./... -test.bench="Benchmark*" > /tmp/after $ benchcmp /tmp/before /tmp/after benchmark old ns/op new ns/op delta BenchmarkEncode1B 1282 936 -26.99% BenchmarkEncode1KiB 4865 4184 -14.00% BenchmarkEncode8KiB 22686 21560 -4.96% BenchmarkEncode64KiB 134451 116762 -13.16% BenchmarkEncode512KiB 514044 361224 -29.73% BenchmarkEncode1MiB 767096 636725 -17.00% benchmark old MB/s new MB/s speedup BenchmarkEncode1B 6.24 8.55 1.37x BenchmarkEncode1KiB 212.11 246.63 1.16x BenchmarkEncode8KiB 361.46 380.33 1.05x BenchmarkEncode64KiB 487.50 561.35 1.15x BenchmarkEncode512KiB 1019.94 1451.45 1.42x BenchmarkEncode1MiB 1366.95 1646.84 1.20x benchmark old allocs new allocs delta BenchmarkEncode1B 6 3 -50.00% BenchmarkEncode1KiB 8 5 -37.50% BenchmarkEncode8KiB 8 5 -37.50% BenchmarkEncode64KiB 8 5 -37.50% BenchmarkEncode512KiB 8 5 -37.50% BenchmarkEncode1MiB 8 5 -37.50% benchmark old bytes new bytes delta BenchmarkEncode1B 384 328 -14.58% BenchmarkEncode1KiB 2816 2760 -1.99% BenchmarkEncode8KiB 17283 17227 -0.32% BenchmarkEncode64KiB 147856 147802 -0.04% BenchmarkEncode512KiB 1065344 1065288 -0.01% BenchmarkEncode1MiB 2113920 2113864 -0.00% ``` ..., which is apropos of the comment in [encoding/binary] (http://golang.org/pkg/encoding/binary), wherein ... > This package favors simplicity over efficiency. ... is stated. If `encode` is deemed to need further memory efficiencies, a mechanism whereby a `proto.Buffer` is retained may be warranted, which is why the original TODO remains. The proposed improvement in this change is simple and low-hanging. I did not want to introduce yet-another protocol buffer message for tests, but the ones under ... > interop/grpc_testing/test.proto > test/grpc_testing/test.proto ... have a fundamental dependency on `grpc` package due to their generated stubs, which produces a cycle in the imports if the benchmark were to attempt to import them for profiling. The newly created ... > test/grpc_message/test.proto ... protocol buffer package has no generated RPC service stubs, which means it can be imported into the `grpc` package root without cycle.
2015-02-28 20:46:39 +03:00
msg := &perfpb.Buffer{Body: make([]byte, mSize)}
encodeData, _ := encode(cdc, msg)
encodedSz := int64(len(encodeData))
Benchmark encode and cut encoding inefficiency. This commit introduces the first microbenchmark for grpc, wherein `encode` is benchmarked according to message size. A conclusion of the benchmark is that the removal of type switching found in `binary.Write`, which is used in `encode` produces the following encoding time and memory allocation footprint: ``` $ # Return to previous commit but benchmark. $ go test ./... -test.bench="Benchmark*" > /tmp/before $ # Return to working copy. $ go test ./... -test.bench="Benchmark*" > /tmp/after $ benchcmp /tmp/before /tmp/after benchmark old ns/op new ns/op delta BenchmarkEncode1B 1282 936 -26.99% BenchmarkEncode1KiB 4865 4184 -14.00% BenchmarkEncode8KiB 22686 21560 -4.96% BenchmarkEncode64KiB 134451 116762 -13.16% BenchmarkEncode512KiB 514044 361224 -29.73% BenchmarkEncode1MiB 767096 636725 -17.00% benchmark old MB/s new MB/s speedup BenchmarkEncode1B 6.24 8.55 1.37x BenchmarkEncode1KiB 212.11 246.63 1.16x BenchmarkEncode8KiB 361.46 380.33 1.05x BenchmarkEncode64KiB 487.50 561.35 1.15x BenchmarkEncode512KiB 1019.94 1451.45 1.42x BenchmarkEncode1MiB 1366.95 1646.84 1.20x benchmark old allocs new allocs delta BenchmarkEncode1B 6 3 -50.00% BenchmarkEncode1KiB 8 5 -37.50% BenchmarkEncode8KiB 8 5 -37.50% BenchmarkEncode64KiB 8 5 -37.50% BenchmarkEncode512KiB 8 5 -37.50% BenchmarkEncode1MiB 8 5 -37.50% benchmark old bytes new bytes delta BenchmarkEncode1B 384 328 -14.58% BenchmarkEncode1KiB 2816 2760 -1.99% BenchmarkEncode8KiB 17283 17227 -0.32% BenchmarkEncode64KiB 147856 147802 -0.04% BenchmarkEncode512KiB 1065344 1065288 -0.01% BenchmarkEncode1MiB 2113920 2113864 -0.00% ``` ..., which is apropos of the comment in [encoding/binary] (http://golang.org/pkg/encoding/binary), wherein ... > This package favors simplicity over efficiency. ... is stated. If `encode` is deemed to need further memory efficiencies, a mechanism whereby a `proto.Buffer` is retained may be warranted, which is why the original TODO remains. The proposed improvement in this change is simple and low-hanging. I did not want to introduce yet-another protocol buffer message for tests, but the ones under ... > interop/grpc_testing/test.proto > test/grpc_testing/test.proto ... have a fundamental dependency on `grpc` package due to their generated stubs, which produces a cycle in the imports if the benchmark were to attempt to import them for profiling. The newly created ... > test/grpc_message/test.proto ... protocol buffer package has no generated RPC service stubs, which means it can be imported into the `grpc` package root without cycle.
2015-02-28 20:46:39 +03:00
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
encode(cdc, msg)
Benchmark encode and cut encoding inefficiency. This commit introduces the first microbenchmark for grpc, wherein `encode` is benchmarked according to message size. A conclusion of the benchmark is that the removal of type switching found in `binary.Write`, which is used in `encode` produces the following encoding time and memory allocation footprint: ``` $ # Return to previous commit but benchmark. $ go test ./... -test.bench="Benchmark*" > /tmp/before $ # Return to working copy. $ go test ./... -test.bench="Benchmark*" > /tmp/after $ benchcmp /tmp/before /tmp/after benchmark old ns/op new ns/op delta BenchmarkEncode1B 1282 936 -26.99% BenchmarkEncode1KiB 4865 4184 -14.00% BenchmarkEncode8KiB 22686 21560 -4.96% BenchmarkEncode64KiB 134451 116762 -13.16% BenchmarkEncode512KiB 514044 361224 -29.73% BenchmarkEncode1MiB 767096 636725 -17.00% benchmark old MB/s new MB/s speedup BenchmarkEncode1B 6.24 8.55 1.37x BenchmarkEncode1KiB 212.11 246.63 1.16x BenchmarkEncode8KiB 361.46 380.33 1.05x BenchmarkEncode64KiB 487.50 561.35 1.15x BenchmarkEncode512KiB 1019.94 1451.45 1.42x BenchmarkEncode1MiB 1366.95 1646.84 1.20x benchmark old allocs new allocs delta BenchmarkEncode1B 6 3 -50.00% BenchmarkEncode1KiB 8 5 -37.50% BenchmarkEncode8KiB 8 5 -37.50% BenchmarkEncode64KiB 8 5 -37.50% BenchmarkEncode512KiB 8 5 -37.50% BenchmarkEncode1MiB 8 5 -37.50% benchmark old bytes new bytes delta BenchmarkEncode1B 384 328 -14.58% BenchmarkEncode1KiB 2816 2760 -1.99% BenchmarkEncode8KiB 17283 17227 -0.32% BenchmarkEncode64KiB 147856 147802 -0.04% BenchmarkEncode512KiB 1065344 1065288 -0.01% BenchmarkEncode1MiB 2113920 2113864 -0.00% ``` ..., which is apropos of the comment in [encoding/binary] (http://golang.org/pkg/encoding/binary), wherein ... > This package favors simplicity over efficiency. ... is stated. If `encode` is deemed to need further memory efficiencies, a mechanism whereby a `proto.Buffer` is retained may be warranted, which is why the original TODO remains. The proposed improvement in this change is simple and low-hanging. I did not want to introduce yet-another protocol buffer message for tests, but the ones under ... > interop/grpc_testing/test.proto > test/grpc_testing/test.proto ... have a fundamental dependency on `grpc` package due to their generated stubs, which produces a cycle in the imports if the benchmark were to attempt to import them for profiling. The newly created ... > test/grpc_message/test.proto ... protocol buffer package has no generated RPC service stubs, which means it can be imported into the `grpc` package root without cycle.
2015-02-28 20:46:39 +03:00
}
b.SetBytes(encodedSz)
}
func BenchmarkEncode1B(b *testing.B) {
bmEncode(b, 1)
}
func BenchmarkEncode1KiB(b *testing.B) {
bmEncode(b, 1024)
}
func BenchmarkEncode8KiB(b *testing.B) {
bmEncode(b, 8*1024)
}
func BenchmarkEncode64KiB(b *testing.B) {
bmEncode(b, 64*1024)
}
func BenchmarkEncode512KiB(b *testing.B) {
bmEncode(b, 512*1024)
}
func BenchmarkEncode1MiB(b *testing.B) {
bmEncode(b, 1024*1024)
}
Use pooled gzip.{Writer,Reader} in gzip{Compressor,Decompressor} (#1217) This change saves a lot of memory by reusing the underlying gzip.{Writer,Reader}, which allocates up to 1.4mb at every instanciation according to [1]. This was fixed by adding a Reset method by to the object at [2]. The amount of memory (and GC time) saved is pretty high, as reported by pprof: flat flat% sum% cum cum% 28.33GB 85.70% 85.70% 32.74GB 99.05% compress/flate.NewWriter flat flat% sum% cum cum% 19.39MB 16.74% 16.74% 22.07MB 19.05% compress/flate.NewWriter And the benchmarks: benchmark old ns/op new ns/op delta BenchmarkGZIPCompressor1B-4 215170 22291 -89.64% BenchmarkGZIPCompressor1KiB-4 225971 27213 -87.96% BenchmarkGZIPCompressor8KiB-4 246696 54785 -77.79% BenchmarkGZIPCompressor64KiB-4 444851 286924 -35.50% BenchmarkGZIPCompressor512KiB-4 2279043 2115863 -7.16% BenchmarkGZIPCompressor1MiB-4 4412989 4258635 -3.50% benchmark old allocs new allocs delta BenchmarkGZIPCompressor1B-4 17 0 -100.00% BenchmarkGZIPCompressor1KiB-4 17 0 -100.00% BenchmarkGZIPCompressor8KiB-4 17 0 -100.00% BenchmarkGZIPCompressor64KiB-4 17 0 -100.00% BenchmarkGZIPCompressor512KiB-4 17 0 -100.00% BenchmarkGZIPCompressor1MiB-4 17 0 -100.00% benchmark old bytes new bytes delta BenchmarkGZIPCompressor1B-4 813872 8 -100.00% BenchmarkGZIPCompressor1KiB-4 813872 16 -100.00% BenchmarkGZIPCompressor8KiB-4 813875 27 -100.00% BenchmarkGZIPCompressor64KiB-4 813918 190 -99.98% BenchmarkGZIPCompressor512KiB-4 814928 1871 -99.77% BenchmarkGZIPCompressor1MiB-4 820889 9735 -98.81% [1] https://github.com/golang/go/issues/6138 [2] https://github.com/golang/go/commit/db12f9d4e406dcab81b476e955c8e119112522fa Signed-off-by: Steeve Morin <steeve.morin@gmail.com>
2017-05-11 02:40:25 +03:00
// bmCompressor benchmarks a compressor of a Protocol Buffer message containing
// mSize bytes.
func bmCompressor(b *testing.B, mSize int, cp Compressor) {
payload := make([]byte, mSize)
cBuf := bytes.NewBuffer(make([]byte, mSize))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
cp.Do(cBuf, payload)
cBuf.Reset()
}
}
func BenchmarkGZIPCompressor1B(b *testing.B) {
bmCompressor(b, 1, NewGZIPCompressor())
}
func BenchmarkGZIPCompressor1KiB(b *testing.B) {
bmCompressor(b, 1024, NewGZIPCompressor())
}
func BenchmarkGZIPCompressor8KiB(b *testing.B) {
bmCompressor(b, 8*1024, NewGZIPCompressor())
}
func BenchmarkGZIPCompressor64KiB(b *testing.B) {
bmCompressor(b, 64*1024, NewGZIPCompressor())
}
func BenchmarkGZIPCompressor512KiB(b *testing.B) {
bmCompressor(b, 512*1024, NewGZIPCompressor())
}
func BenchmarkGZIPCompressor1MiB(b *testing.B) {
bmCompressor(b, 1024*1024, NewGZIPCompressor())
}