1 #region Copyright notice and license 2 3 // Copyright 2019 The gRPC Authors 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #endregion 18 19 using System.Threading.Tasks; 20 using BenchmarkDotNet.Attributes; 21 using Grpc.Core; 22 using Grpc.Core.Internal; 23 using System; 24 25 namespace Grpc.Microbenchmarks 26 { 27 // this test measures the overhead of C# wrapping layer when invoking calls; 28 // the marshallers **DO NOT ALLOCATE**, so any allocations 29 // are from the framework, not the messages themselves 30 31 [ClrJob, CoreJob] // test .NET Core and .NET Framework 32 [MemoryDiagnoser] // allocations 33 public class UnaryCallOverheadBenchmark 34 { 35 private static readonly Task<string> CompletedString = Task.FromResult(""); 36 private static readonly Marshaller<byte[]> IdentityMarshaller = new Marshaller<byte[]>(msg => msg, payload => payload); 37 private static readonly Method<byte[], byte[]> PingMethod = new Method<byte[], byte[]>(MethodType.Unary, nameof(PingBenchmark), "Ping", IdentityMarshaller, IdentityMarshaller); 38 39 private int payloadSize; 40 private byte[] payload; 41 42 // size of payload that is sent as request and received as response. 43 [Params(0, 1, 10, 100, 1000)] 44 public int PayloadSize 45 { 46 get { return payloadSize; } 47 set 48 { 49 payloadSize = value; 50 payload = new byte[value]; 51 } 52 } 53 54 [Benchmark] SyncUnaryCallOverhead()55 public byte[] SyncUnaryCallOverhead() 56 { 57 return client.Ping(payload, new CallOptions()); 58 } 59 60 Channel channel; 61 PingClient client; 62 63 [GlobalSetup] Setup()64 public void Setup() 65 { 66 // create client, the channel will actually never connect because call logic will be short-circuited 67 channel = new Channel("localhost", 10042, ChannelCredentials.Insecure); 68 client = new PingClient(new DefaultCallInvoker(channel)); 69 70 var native = NativeMethods.Get(); 71 72 // replace the implementation of a native method with a fake 73 NativeMethods.Delegates.grpcsharp_call_start_unary_delegate fakeCallStartUnary = (CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags) => { 74 return native.grpcsharp_test_call_start_unary_echo(call, ctx, sendBuffer, writeFlags, metadataArray, metadataFlags); 75 }; 76 native.GetType().GetField(nameof(native.grpcsharp_call_start_unary)).SetValue(native, fakeCallStartUnary); 77 78 NativeMethods.Delegates.grpcsharp_completion_queue_pluck_delegate fakeCqPluck = (CompletionQueueSafeHandle cq, IntPtr tag) => { 79 return new CompletionQueueEvent { 80 type = CompletionQueueEvent.CompletionType.OpComplete, 81 success = 1, 82 tag = tag 83 }; 84 }; 85 native.GetType().GetField(nameof(native.grpcsharp_completion_queue_pluck)).SetValue(native, fakeCqPluck); 86 } 87 88 [GlobalCleanup] Cleanup()89 public async Task Cleanup() 90 { 91 await channel.ShutdownAsync(); 92 } 93 94 class PingClient : ClientBase 95 { PingClient(CallInvoker callInvoker)96 public PingClient(CallInvoker callInvoker) : base(callInvoker) { } 97 Ping(byte[] request, CallOptions options)98 public byte[] Ping(byte[] request, CallOptions options) 99 { 100 return CallInvoker.BlockingUnaryCall(PingMethod, null, options, request); 101 } 102 } 103 } 104 } 105