grpc-go/rpc_util_test.go

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

2015-02-06 04:14:05 +03:00
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
2015-02-06 04:14:05 +03:00
import (
"bytes"
"io"
2016-07-30 02:19:20 +03:00
"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/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"
"google.golang.org/grpc/transport"
2015-02-06 04:14:05 +03:00
)
func TestSimpleParsing(t *testing.T) {
bigMsg := bytes.Repeat([]byte{'x'}, 1<<24)
2015-02-06 04:14:05 +03:00
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},
2015-02-06 04:14:05 +03:00
} {
buf := bytes.NewReader(test.p)
parser := &parser{r: buf}
2016-07-30 02:19:20 +03:00
pt, b, err := parser.recvMsg(math.MaxInt32)
2015-02-06 04:14:05 +03:00
if err != test.err || !bytes.Equal(b, test.b) || pt != test.pt {
2016-07-30 02:19:20 +03:00
t.Fatalf("parser{%v}.recvMsg(_) = %v, %v, %v\nwant %v, %v, %v", test.p, pt, b, err, test.pt, test.b, test.err)
2015-02-06 04:14:05 +03:00
}
}
}
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 := bytes.NewReader(p)
parser := &parser{r: b}
2015-02-06 04:14:05 +03:00
wantRecvs := []struct {
pt payloadFormat
data []byte
}{
{compressionNone, []byte("a")},
{compressionNone, []byte("bc")},
{compressionNone, []byte("d")},
}
for i, want := range wantRecvs {
2016-07-30 02:19:20 +03:00
pt, data, err := parser.recvMsg(math.MaxInt32)
2015-02-06 04:14:05 +03:00
if err != nil || pt != want.pt || !reflect.DeepEqual(data, want.data) {
2016-07-30 02:19:20 +03:00
t.Fatalf("after %d calls, parser{%v}.recvMsg(_) = %v, %v, %v\nwant %v, %v, <nil>",
2015-02-06 04:14:05 +03:00
i, p, pt, data, err, want.pt, want.data)
}
}
2016-07-30 02:19:20 +03:00
pt, data, err := parser.recvMsg(math.MaxInt32)
2015-02-06 04:14:05 +03:00
if err != io.EOF {
2016-07-30 02:19:20 +03:00
t.Fatalf("after %d recvMsgs calls, parser{%v}.recvMsg(_) = %v, %v, %v\nwant _, _, %v",
2015-02-06 04:14:05 +03:00
len(wantRecvs), p, pt, data, err, io.EOF)
}
}
func TestEncode(t *testing.T) {
for _, test := range []struct {
// input
msg proto.Message
2016-01-23 05:21:41 +03:00
cp Compressor
2015-02-06 04:14:05 +03:00
// outputs
b []byte
err error
}{
2016-01-23 05:21:41 +03:00
{nil, nil, []byte{0, 0, 0, 0, 0}, nil},
2015-02-06 04:14:05 +03:00
} {
b, err := encode(protoCodec{}, test.msg, nil, nil, nil)
2015-02-06 04:14:05 +03:00
if err != test.err || !bytes.Equal(b, test.b) {
2016-01-23 05:21:41 +03:00
t.Fatalf("encode(_, _, %v, _) = %v, %v\nwant %v, %v", test.cp, b, err, test.b, test.err)
}
}
}
func TestCompress(t *testing.T) {
for _, test := range []struct {
// input
data []byte
cp Compressor
dc Decompressor
// outputs
err error
}{
{make([]byte, 1024), &gzipCompressor{}, &gzipDecompressor{}, nil},
} {
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.StreamError{Code: codes.Unknown, Desc: ""}, status.Error(codes.Unknown, "")},
{transport.ErrConnClosing, status.Error(codes.Internal, transport.ErrConnClosing.Desc)},
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)
}
}
}
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) {
msg := &perfpb.Buffer{Body: make([]byte, mSize)}
encoded, _ := encode(protoCodec{}, msg, nil, nil, nil)
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
encodedSz := int64(len(encoded))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
encode(protoCodec{}, msg, nil, nil, nil)
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)
}