Merge pull request #13475 from jtattermusch/better_completion_registry_benchmark

Use spinlock in CompletionRegistry (and improve the benchmark).
pull/13452/head^2
Jan Tattermusch 7 years ago committed by GitHub
commit 1304cf8174
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
  2. 13
      src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs
  3. 5
      src/csharp/Grpc.Microbenchmarks/Program.cs

@ -19,7 +19,9 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using Grpc.Core.Logging; using Grpc.Core.Logging;
using Grpc.Core.Utils; using Grpc.Core.Utils;
@ -35,7 +37,7 @@ namespace Grpc.Core.Internal
readonly GrpcEnvironment environment; readonly GrpcEnvironment environment;
readonly Dictionary<IntPtr, IOpCompletionCallback> dict = new Dictionary<IntPtr, IOpCompletionCallback>(new IntPtrComparer()); readonly Dictionary<IntPtr, IOpCompletionCallback> dict = new Dictionary<IntPtr, IOpCompletionCallback>(new IntPtrComparer());
readonly object myLock = new object(); SpinLock spinLock = new SpinLock(Debugger.IsAttached);
IntPtr lastRegisteredKey; // only for testing IntPtr lastRegisteredKey; // only for testing
public CompletionRegistry(GrpcEnvironment environment) public CompletionRegistry(GrpcEnvironment environment)
@ -46,11 +48,19 @@ namespace Grpc.Core.Internal
public void Register(IntPtr key, IOpCompletionCallback callback) public void Register(IntPtr key, IOpCompletionCallback callback)
{ {
environment.DebugStats.PendingBatchCompletions.Increment(); environment.DebugStats.PendingBatchCompletions.Increment();
lock (myLock)
bool lockTaken = false;
try
{ {
spinLock.Enter(ref lockTaken);
dict.Add(key, callback); dict.Add(key, callback);
this.lastRegisteredKey = key; this.lastRegisteredKey = key;
} }
finally
{
if (lockTaken) spinLock.Exit();
}
} }
public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback, object state) public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback, object state)
@ -68,11 +78,18 @@ namespace Grpc.Core.Internal
public IOpCompletionCallback Extract(IntPtr key) public IOpCompletionCallback Extract(IntPtr key)
{ {
IOpCompletionCallback value = null; IOpCompletionCallback value = null;
lock (myLock) bool lockTaken = false;
try
{ {
spinLock.Enter(ref lockTaken);
value = dict[key]; value = dict[key];
dict.Remove(key); dict.Remove(key);
} }
finally
{
if (lockTaken) spinLock.Exit();
}
environment.DebugStats.PendingBatchCompletions.Decrement(); environment.DebugStats.PendingBatchCompletions.Decrement();
return value; return value;
} }

@ -40,24 +40,25 @@ namespace Grpc.Microbenchmarks
GrpcEnvironment.ReleaseAsync().Wait(); GrpcEnvironment.ReleaseAsync().Wait();
} }
public void Run(int threadCount, int iterations) public void Run(int threadCount, int iterations, bool useSharedRegistry)
{ {
Console.WriteLine(string.Format("CompletionRegistryBenchmark: threads={0}, iterations={1}", threadCount, iterations)); Console.WriteLine(string.Format("CompletionRegistryBenchmark: threads={0}, iterations={1}, useSharedRegistry={2}", threadCount, iterations, useSharedRegistry));
var threadedBenchmark = new ThreadedBenchmark(threadCount, () => ThreadBody(iterations)); CompletionRegistry sharedRegistry = useSharedRegistry ? new CompletionRegistry(environment) : null;
var threadedBenchmark = new ThreadedBenchmark(threadCount, () => ThreadBody(iterations, sharedRegistry));
threadedBenchmark.Run(); threadedBenchmark.Run();
// TODO: parametrize by number of pending completions // TODO: parametrize by number of pending completions
} }
private void ThreadBody(int iterations) private void ThreadBody(int iterations, CompletionRegistry optionalSharedRegistry)
{ {
var completionRegistry = new CompletionRegistry(environment); var completionRegistry = optionalSharedRegistry ?? new CompletionRegistry(environment);
var ctx = BatchContextSafeHandle.Create(); var ctx = BatchContextSafeHandle.Create();
var stopwatch = Stopwatch.StartNew(); var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) for (int i = 0; i < iterations; i++)
{ {
completionRegistry.Register(ctx.Handle, ctx); completionRegistry.Register(ctx.Handle, ctx);
var callback = completionRegistry.Extract(completionRegistry.LastRegisteredKey); var callback = completionRegistry.Extract(ctx.Handle);
// NOTE: we are not calling the callback to avoid disposing ctx. // NOTE: we are not calling the callback to avoid disposing ctx.
} }
stopwatch.Stop(); stopwatch.Stop();

@ -77,7 +77,10 @@ namespace Grpc.Microbenchmarks
benchmark.Init(); benchmark.Init();
foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12}) foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12})
{ {
benchmark.Run(threadCount, 4 * 1000 * 1000); foreach (bool useSharedRegistry in new bool[] {false, true})
{
benchmark.Run(threadCount, 4 * 1000 * 1000, useSharedRegistry);
}
} }
benchmark.Cleanup(); benchmark.Cleanup();
} }

Loading…
Cancel
Save