mirror of https://github.com/grpc/grpc.git
Merge branch 'master' of https://github.com/grpc/grpc into grpc_to_grpc_impl_async_callback
commit
a67cd9c362
20 changed files with 471 additions and 338 deletions
@ -0,0 +1,66 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2015 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; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using BenchmarkDotNet.Attributes; |
||||
using Grpc.Core; |
||||
|
||||
namespace Grpc.Microbenchmarks |
||||
{ |
||||
|
||||
// common base-type for tests that need to run with some level of concurrency; |
||||
// note there's nothing *special* about this type - it is just to save some |
||||
// boilerplate |
||||
|
||||
[ClrJob, CoreJob] // test .NET Core and .NET Framework |
||||
[MemoryDiagnoser] // allocations |
||||
public abstract class CommonThreadedBase |
||||
{ |
||||
protected virtual bool NeedsEnvironment => true; |
||||
|
||||
[Params(1, 2, 4, 8, 12)] |
||||
public int ThreadCount { get; set; } |
||||
|
||||
protected GrpcEnvironment Environment { get; private set; } |
||||
|
||||
[GlobalSetup] |
||||
public virtual void Setup() |
||||
{ |
||||
ThreadPool.GetMinThreads(out var workers, out var iocp); |
||||
if (workers <= ThreadCount) ThreadPool.SetMinThreads(ThreadCount + 1, iocp); |
||||
if (NeedsEnvironment) Environment = GrpcEnvironment.AddRef(); |
||||
} |
||||
|
||||
[GlobalCleanup] |
||||
public virtual void Cleanup() |
||||
{ |
||||
if (Environment != null) |
||||
{ |
||||
Environment = null; |
||||
GrpcEnvironment.ReleaseAsync().Wait(); |
||||
} |
||||
} |
||||
|
||||
protected void RunConcurrent(Action operation) |
||||
{ |
||||
Parallel.For(0, ThreadCount, _ => operation()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,102 @@ |
||||
#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; |
||||
|
||||
namespace Grpc.Microbenchmarks |
||||
{ |
||||
// this test creates a real server and client, measuring the inherent inbuilt |
||||
// platform overheads; the marshallers **DO NOT ALLOCATE**, so any allocations |
||||
// are from the framework, not the messages themselves |
||||
|
||||
// important: allocs are not reliable on .NET Core until .NET Core 3, since |
||||
// this test involves multiple threads |
||||
|
||||
[ClrJob, CoreJob] // test .NET Core and .NET Framework |
||||
[MemoryDiagnoser] // allocations |
||||
public class PingBenchmark |
||||
{ |
||||
private static readonly Task<string> CompletedString = Task.FromResult(""); |
||||
private static readonly byte[] EmptyBlob = new byte[0]; |
||||
private static readonly Marshaller<string> EmptyMarshaller = new Marshaller<string>(_ => EmptyBlob, _ => ""); |
||||
private static readonly Method<string, string> PingMethod = new Method<string, string>(MethodType.Unary, nameof(PingBenchmark), "Ping", EmptyMarshaller, EmptyMarshaller); |
||||
|
||||
|
||||
[Benchmark] |
||||
public async ValueTask<string> PingAsync() |
||||
{ |
||||
using (var result = client.PingAsync("", new CallOptions())) |
||||
{ |
||||
return await result.ResponseAsync; |
||||
} |
||||
} |
||||
|
||||
[Benchmark] |
||||
public string Ping() |
||||
{ |
||||
return client.Ping("", new CallOptions()); |
||||
} |
||||
|
||||
private Task<string> ServerMethod(string request, ServerCallContext context) |
||||
{ |
||||
return CompletedString; |
||||
} |
||||
|
||||
Server server; |
||||
Channel channel; |
||||
PingClient client; |
||||
|
||||
[GlobalSetup] |
||||
public async Task Setup() |
||||
{ |
||||
// create server |
||||
server = new Server { |
||||
Ports = { new ServerPort("localhost", 10042, ServerCredentials.Insecure) }, |
||||
Services = { ServerServiceDefinition.CreateBuilder().AddMethod(PingMethod, ServerMethod).Build() }, |
||||
}; |
||||
server.Start(); |
||||
|
||||
// create client |
||||
channel = new Channel("localhost", 10042, ChannelCredentials.Insecure); |
||||
await channel.ConnectAsync(); |
||||
client = new PingClient(new DefaultCallInvoker(channel)); |
||||
} |
||||
|
||||
[GlobalCleanup] |
||||
public async Task Cleanup() |
||||
{ |
||||
await channel.ShutdownAsync(); |
||||
await server.ShutdownAsync(); |
||||
} |
||||
|
||||
class PingClient : LiteClientBase |
||||
{ |
||||
public PingClient(CallInvoker callInvoker) : base(callInvoker) { } |
||||
public AsyncUnaryCall<string> PingAsync(string request, CallOptions options) |
||||
{ |
||||
return CallInvoker.AsyncUnaryCall(PingMethod, null, options, request); |
||||
} |
||||
public string Ping(string request, CallOptions options) |
||||
{ |
||||
return CallInvoker.BlockingUnaryCall(PingMethod, null, options, request); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,70 @@ |
||||
#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; |
||||
using System.Collections.Generic; |
||||
using System.Text; |
||||
using BenchmarkDotNet.Attributes; |
||||
using Grpc.Core.Internal; |
||||
|
||||
namespace Grpc.Microbenchmarks |
||||
{ |
||||
[ClrJob, CoreJob] // test .NET Core and .NET Framework |
||||
[MemoryDiagnoser] // allocations |
||||
public class Utf8Decode |
||||
{ |
||||
[Params(0, 1, 4, 128, 1024)] |
||||
public int PayloadSize |
||||
{ |
||||
get { return payloadSize; } |
||||
set |
||||
{ |
||||
payloadSize = value; |
||||
payload = Invent(value); |
||||
} |
||||
} |
||||
|
||||
private int payloadSize; |
||||
private byte[] payload; |
||||
|
||||
static byte[] Invent(int length) |
||||
{ |
||||
var rand = new Random(Seed: length); |
||||
var chars = new char[length]; |
||||
for(int i = 0; i < chars.Length; i++) |
||||
{ |
||||
chars[i] = (char)rand.Next(32, 300); |
||||
} |
||||
return Encoding.UTF8.GetBytes(chars); |
||||
} |
||||
|
||||
const int Iterations = 1000; |
||||
[Benchmark(OperationsPerInvoke = Iterations)] |
||||
public unsafe void Decode() |
||||
{ |
||||
fixed (byte* ptr = payload) |
||||
{ |
||||
var iPtr = new IntPtr(ptr); |
||||
for (int i = 0; i < Iterations; i++) |
||||
{ |
||||
MarshalUtils.PtrToStringUTF8(iPtr, payload.Length); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,126 @@ |
||||
#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; |
||||
using System.Collections.Generic; |
||||
using BenchmarkDotNet.Attributes; |
||||
using Grpc.Core; |
||||
using Grpc.Core.Internal; |
||||
|
||||
namespace Grpc.Microbenchmarks |
||||
{ |
||||
[ClrJob, CoreJob] // test .NET Core and .NET Framework |
||||
[MemoryDiagnoser] // allocations |
||||
public class Utf8Encode : ISendStatusFromServerCompletionCallback |
||||
{ |
||||
[Params(0, 1, 4, 128, 1024)] |
||||
public int PayloadSize |
||||
{ |
||||
get { return payloadSize; } |
||||
set |
||||
{ |
||||
payloadSize = value; |
||||
status = new Status(StatusCode.OK, Invent(value)); |
||||
} |
||||
} |
||||
|
||||
private int payloadSize; |
||||
private Status status; |
||||
|
||||
static string Invent(int length) |
||||
{ |
||||
var rand = new Random(Seed: length); |
||||
var chars = new char[length]; |
||||
for(int i = 0; i < chars.Length; i++) |
||||
{ |
||||
chars[i] = (char)rand.Next(32, 300); |
||||
} |
||||
return new string(chars); |
||||
} |
||||
|
||||
private GrpcEnvironment environment; |
||||
private CompletionRegistry completionRegistry; |
||||
[GlobalSetup] |
||||
public void Setup() |
||||
{ |
||||
var native = NativeMethods.Get(); |
||||
|
||||
// nop the native-call via reflection |
||||
NativeMethods.Delegates.grpcsharp_call_send_status_from_server_delegate nop = (CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags) => { |
||||
completionRegistry.Extract(ctx.Handle).OnComplete(true); // drain the dictionary as we go |
||||
return CallError.OK; |
||||
}; |
||||
native.GetType().GetField(nameof(native.grpcsharp_call_send_status_from_server)).SetValue(native, nop); |
||||
|
||||
environment = GrpcEnvironment.AddRef(); |
||||
metadata = MetadataArraySafeHandle.Create(Metadata.Empty); |
||||
completionRegistry = new CompletionRegistry(environment, () => environment.BatchContextPool.Lease(), () => throw new NotImplementedException()); |
||||
var cq = CompletionQueueSafeHandle.CreateAsync(completionRegistry); |
||||
call = CreateFakeCall(cq); |
||||
} |
||||
|
||||
private static CallSafeHandle CreateFakeCall(CompletionQueueSafeHandle cq) |
||||
{ |
||||
var call = CallSafeHandle.CreateFake(new IntPtr(0xdead), cq); |
||||
bool success = false; |
||||
while (!success) |
||||
{ |
||||
// avoid calling destroy on a nonexistent grpc_call pointer |
||||
call.DangerousAddRef(ref success); |
||||
} |
||||
return call; |
||||
} |
||||
|
||||
[GlobalCleanup] |
||||
public void Cleanup() |
||||
{ |
||||
try |
||||
{ |
||||
metadata?.Dispose(); |
||||
metadata = null; |
||||
call?.Dispose(); |
||||
call = null; |
||||
|
||||
if (environment != null) |
||||
{ |
||||
environment = null; |
||||
// cleanup seems... unreliable on CLR |
||||
// GrpcEnvironment.ReleaseAsync().Wait(1000); |
||||
} |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
Console.Error.WriteLine(ex.Message); |
||||
} |
||||
} |
||||
private CallSafeHandle call; |
||||
private MetadataArraySafeHandle metadata; |
||||
|
||||
const int Iterations = 1000; |
||||
[Benchmark(OperationsPerInvoke = Iterations)] |
||||
public unsafe void SendStatus() |
||||
{ |
||||
for (int i = 0; i < Iterations; i++) |
||||
{ |
||||
call.StartSendStatusFromServer(this, status, metadata, false, null, WriteFlags.NoCompress); |
||||
} |
||||
} |
||||
|
||||
void ISendStatusFromServerCompletionCallback.OnSendStatusFromServerCompletion(bool success) { } |
||||
} |
||||
} |
Loading…
Reference in new issue