Skip to content

Commit

Permalink
contrib/google.golang.org/grpc: benchmark for unary interceptor (#1133)
Browse files Browse the repository at this point in the history
This benchmark attempts calls the gRPC interceptor with a few
different set of inputs, to try and measure the current overhead of
this operation. If this looks good, the goal would be to use this to
confirm that some small tweaks actually have the desired effect.

This was motivated when I noticed that the Datadog gRPC interceptor
is about 0.5% of total CPU time, and about 0.4% of all allocations
in one production application, as measured by the Datadog continuous
profiler. I think there are a few easy small wins that are possible.
I want to make sure we can measure the impact of these changes.
  • Loading branch information
evanj authored Jan 26, 2022
1 parent 56992a1 commit dcf8af1
Showing 1 changed file with 76 additions and 0 deletions.
76 changes: 76 additions & 0 deletions contrib/google.golang.org/grpc/grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig"
"gopkg.in/DataDog/dd-trace-go.v1/internal/log"

"github.com/stretchr/testify/assert"
context "golang.org/x/net/context"
Expand Down Expand Up @@ -795,3 +796,78 @@ func TestIgnoredMetadata(t *testing.T) {
mt.Reset()
}
}

func BenchmarkUnaryServerInterceptor(b *testing.B) {
// need to use the real tracer to get representative measurments
tracer.Start(tracer.WithLogger(log.DiscardLogger{}))
defer tracer.Stop()

doNothingOKGRPCHandler := func(ctx context.Context, req interface{}) (interface{}, error) {
return nil, nil
}

unknownErr := status.Error(codes.Unknown, "some unknown error")
doNothingErrorGRPCHandler := func(ctx context.Context, req interface{}) (interface{}, error) {
return nil, unknownErr
}

// Add gRPC metadata to ctx to get resonably accurate performance numbers. From a production
// application, there can be quite a few key/value pairs. A number of these are added by
// gRPC itself, and others by Datadog tracing
md := metadata.Pairs(
":authority", "example-service-name.example.com:12345",
"content-type", "application/grpc",
"user-agent", "grpc-go/1.32.0",
"x-datadog-sampling-priority", "1",
)
mdWithParent := metadata.Join(md, metadata.Pairs(
"x-datadog-trace-id", "9219028207762307503",
"x-datadog-parent-id", "7525005002014855056",
))
ctx := context.Background()
ctxWithMetadataNoParent := metadata.NewIncomingContext(ctx, md)
ctxWithMetadataWithParent := metadata.NewIncomingContext(ctx, mdWithParent)

methodInfo := &grpc.UnaryServerInfo{FullMethod: "/package.MyService/ExampleMethod"}
interceptor := UnaryServerInterceptor()
b.Run("ok_no_metadata", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
interceptor(ctx, "ignoredRequestValue", methodInfo, doNothingOKGRPCHandler)
}
})

b.Run("ok_with_metadata_no_parent", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
interceptor(ctxWithMetadataNoParent, "ignoredRequestValue", methodInfo, doNothingOKGRPCHandler)
}
})

b.Run("ok_with_metadata_with_parent", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
interceptor(ctxWithMetadataWithParent, "ignoredRequestValue", methodInfo, doNothingOKGRPCHandler)
}
})

interceptorWithRate := UnaryServerInterceptor(WithAnalyticsRate(0.5))
b.Run("ok_no_metadata_with_analytics_rate", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
interceptorWithRate(ctx, "ignoredRequestValue", methodInfo, doNothingOKGRPCHandler)
}
})

b.Run("error_no_metadata", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
interceptor(ctx, "ignoredRequestValue", methodInfo, doNothingErrorGRPCHandler)
}
})
}

0 comments on commit dcf8af1

Please sign in to comment.