blob: fe193b736486e7ac2cb1a25ac8f22cb0e2237b26 [file] [log] [blame]
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// 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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// 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.
#endregion
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Grpc.Core;
using Grpc.Core.Internal;
using System;
namespace Grpc.Microbenchmarks
{
// this test measures the overhead of C# wrapping layer when invoking calls;
// the marshallers **DO NOT ALLOCATE**, so any allocations
// are from the framework, not the messages themselves
[ClrJob, CoreJob] // test .NET Core and .NET Framework
[MemoryDiagnoser] // allocations
public class UnaryCallOverheadBenchmark
{
private static readonly Task<string> CompletedString = Task.FromResult("");
private static readonly Marshaller<byte[]> IdentityMarshaller = new Marshaller<byte[]>(msg => msg, payload => payload);
private static readonly Method<byte[], byte[]> PingMethod = new Method<byte[], byte[]>(MethodType.Unary, nameof(PingBenchmark), "Ping", IdentityMarshaller, IdentityMarshaller);
private int payloadSize;
private byte[] payload;
// size of payload that is sent as request and received as response.
[Params(0, 1, 10, 100, 1000)]
public int PayloadSize
{
get { return payloadSize; }
set
{
payloadSize = value;
payload = new byte[value];
}
}
[Benchmark]
public byte[] SyncUnaryCallOverhead()
{
return client.Ping(payload, new CallOptions());
}
Channel channel;
PingClient client;
[GlobalSetup]
public void Setup()
{
// create client, the channel will actually never connect because call logic will be short-circuited
channel = new Channel("localhost", 10042, ChannelCredentials.Insecure);
client = new PingClient(new DefaultCallInvoker(channel));
var native = NativeMethods.Get();
// replace the implementation of a native method with a fake
NativeMethods.Delegates.grpcsharp_call_start_unary_delegate fakeCallStartUnary = (CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags) => {
return native.grpcsharp_test_call_start_unary_echo(call, ctx, sendBuffer, writeFlags, metadataArray, metadataFlags);
};
native.GetType().GetField(nameof(native.grpcsharp_call_start_unary)).SetValue(native, fakeCallStartUnary);
NativeMethods.Delegates.grpcsharp_completion_queue_pluck_delegate fakeCqPluck = (CompletionQueueSafeHandle cq, IntPtr tag) => {
return new CompletionQueueEvent {
type = CompletionQueueEvent.CompletionType.OpComplete,
success = 1,
tag = tag
};
};
native.GetType().GetField(nameof(native.grpcsharp_completion_queue_pluck)).SetValue(native, fakeCqPluck);
}
[GlobalCleanup]
public async Task Cleanup()
{
await channel.ShutdownAsync();
}
class PingClient : ClientBase
{
public PingClient(CallInvoker callInvoker) : base(callInvoker) { }
public byte[] Ping(byte[] request, CallOptions options)
{
return CallInvoker.BlockingUnaryCall(PingMethod, null, options, request);
}
}
}
}