Merge pull request #4025 from jtattermusch/csharp_perf_instrumentation

Add simple profiling instrumentation for C#
pull/4031/head
Michael Lumish 9 years ago
commit e146e0c7dc
  1. 14
      src/csharp/Grpc.Core.Tests/ClientServerTest.cs
  2. 1
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  3. 19
      src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs
  4. 99
      src/csharp/Grpc.Core.Tests/PerformanceTest.cs
  5. 7
      src/csharp/Grpc.Core/Grpc.Core.csproj
  6. 24
      src/csharp/Grpc.Core/Internal/AsyncCall.cs
  7. 12
      src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
  8. 4
      src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
  9. 4
      src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
  10. 4
      src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
  11. 3
      src/csharp/Grpc.Core/Internal/Enums.cs
  12. 4
      src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
  13. 13
      src/csharp/Grpc.Core/Internal/Timespec.cs
  14. 47
      src/csharp/Grpc.Core/Profiling/IProfiler.cs
  15. 87
      src/csharp/Grpc.Core/Profiling/ProfilerEntry.cs
  16. 60
      src/csharp/Grpc.Core/Profiling/ProfilerScope.cs
  17. 131
      src/csharp/Grpc.Core/Profiling/Profilers.cs

@ -38,6 +38,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Grpc.Core; using Grpc.Core;
using Grpc.Core.Internal; using Grpc.Core.Internal;
using Grpc.Core.Profiling;
using Grpc.Core.Utils; using Grpc.Core.Utils;
using NUnit.Framework; using NUnit.Framework;
@ -201,19 +202,6 @@ namespace Grpc.Core.Tests
CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes); CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes);
} }
[Test]
public void UnaryCallPerformance()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
return request;
});
var callDetails = helper.CreateUnaryCall();
BenchmarkUtil.RunBenchmark(1, 10,
() => { Calls.BlockingUnaryCall(callDetails, "ABC"); });
}
[Test] [Test]
public void UnknownMethodHandler() public void UnknownMethodHandler()
{ {

@ -88,6 +88,7 @@
<Compile Include="CompressionTest.cs" /> <Compile Include="CompressionTest.cs" />
<Compile Include="ContextPropagationTest.cs" /> <Compile Include="ContextPropagationTest.cs" />
<Compile Include="MetadataTest.cs" /> <Compile Include="MetadataTest.cs" />
<Compile Include="PerformanceTest.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>

@ -34,6 +34,7 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Grpc.Core.Internal; using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework; using NUnit.Framework;
namespace Grpc.Core.Internal.Tests namespace Grpc.Core.Internal.Tests
@ -198,5 +199,23 @@ namespace Grpc.Core.Internal.Tests
Console.WriteLine("Test cannot be run on this platform, skipping the test."); Console.WriteLine("Test cannot be run on this platform, skipping the test.");
} }
} }
// Test attribute commented out to prevent running as part of the default test suite.
// [Test]
// [Category("Performance")]
public void NowBenchmark()
{
// approx Timespec.Now latency <33ns
BenchmarkUtil.RunBenchmark(10000000, 1000000000, () => { var now = Timespec.Now; });
}
// Test attribute commented out to prevent running as part of the default test suite.
// [Test]
// [Category("Performance")]
public void PreciseNowBenchmark()
{
// approx Timespec.PreciseNow latency <18ns (when compiled with GRPC_TIMERS_RDTSC)
BenchmarkUtil.RunBenchmark(10000000, 1000000000, () => { var now = Timespec.PreciseNow; });
}
} }
} }

@ -0,0 +1,99 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Profiling;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class PerformanceTest
{
const string Host = "127.0.0.1";
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
helper = new MockServiceHelper(Host);
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
}
// Test attribute commented out to prevent running as part of the default test suite.
//[Test]
//[Category("Performance")]
public void UnaryCallPerformance()
{
var profiler = new BasicProfiler();
Profilers.SetForCurrentThread(profiler);
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
return request;
});
var callDetails = helper.CreateUnaryCall();
for(int i = 0; i < 3000; i++)
{
Calls.BlockingUnaryCall(callDetails, "ABC");
}
profiler.Reset();
for(int i = 0; i < 3000; i++)
{
Calls.BlockingUnaryCall(callDetails, "ABC");
}
profiler.Dump("latency_trace_csharp.txt");
}
}
}

@ -119,6 +119,10 @@
<Compile Include="CompressionLevel.cs" /> <Compile Include="CompressionLevel.cs" />
<Compile Include="WriteOptions.cs" /> <Compile Include="WriteOptions.cs" />
<Compile Include="ContextPropagationToken.cs" /> <Compile Include="ContextPropagationToken.cs" />
<Compile Include="Profiling\ProfilerEntry.cs" />
<Compile Include="Profiling\ProfilerScope.cs" />
<Compile Include="Profiling\IProfiler.cs" />
<Compile Include="Profiling\Profilers.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Grpc.Core.nuspec" /> <None Include="Grpc.Core.nuspec" />
@ -150,4 +154,7 @@
<Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" /> <Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" />
<Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" /> <Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" />
<ItemGroup /> <ItemGroup />
<ItemGroup>
<Folder Include="Profiling\" />
</ItemGroup>
</Project> </Project>

@ -39,6 +39,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Grpc.Core.Internal; using Grpc.Core.Internal;
using Grpc.Core.Logging; using Grpc.Core.Logging;
using Grpc.Core.Profiling;
using Grpc.Core.Utils; using Grpc.Core.Utils;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
@ -87,6 +88,9 @@ namespace Grpc.Core.Internal
/// </summary> /// </summary>
public TResponse UnaryCall(TRequest msg) public TResponse UnaryCall(TRequest msg)
{ {
var profiler = Profilers.ForCurrentThread();
using (profiler.NewScope("AsyncCall.UnaryCall"))
using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create()) using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create())
{ {
byte[] payload = UnsafeSerialize(msg); byte[] payload = UnsafeSerialize(msg);
@ -104,23 +108,25 @@ namespace Grpc.Core.Internal
} }
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
using (var ctx = BatchContextSafeHandle.Create()) using (var ctx = BatchContextSafeHandle.Create())
{ {
call.StartUnary(ctx, payload, metadataArray, GetWriteFlagsForCall()); call.StartUnary(ctx, payload, metadataArray, GetWriteFlagsForCall());
var ev = cq.Pluck(ctx.Handle); var ev = cq.Pluck(ctx.Handle);
bool success = (ev.success != 0); bool success = (ev.success != 0);
try try
{
using (profiler.NewScope("AsyncCall.UnaryCall.HandleBatch"))
{ {
HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessage(), ctx.GetReceivedInitialMetadata()); HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessage(), ctx.GetReceivedInitialMetadata());
} }
}
catch (Exception e) catch (Exception e)
{ {
Logger.Error(e, "Exception occured while invoking completion delegate."); Logger.Error(e, "Exception occured while invoking completion delegate.");
} }
} }
}
// Once the blocking call returns, the result should be available synchronously. // Once the blocking call returns, the result should be available synchronously.
// Note that GetAwaiter().GetResult() doesn't wrap exceptions in AggregateException. // Note that GetAwaiter().GetResult() doesn't wrap exceptions in AggregateException.
@ -328,14 +334,20 @@ namespace Grpc.Core.Internal
} }
private void Initialize(CompletionQueueSafeHandle cq) private void Initialize(CompletionQueueSafeHandle cq)
{
using (Profilers.ForCurrentThread().NewScope("AsyncCall.Initialize"))
{ {
var call = CreateNativeCall(cq); var call = CreateNativeCall(cq);
details.Channel.AddCallReference(this); details.Channel.AddCallReference(this);
InitializeInternal(call); InitializeInternal(call);
RegisterCancellationCallback(); RegisterCancellationCallback();
} }
}
private INativeCall CreateNativeCall(CompletionQueueSafeHandle cq) private INativeCall CreateNativeCall(CompletionQueueSafeHandle cq)
{
using (Profilers.ForCurrentThread().NewScope("AsyncCall.CreateNativeCall"))
{ {
if (injectedNativeCall != null) if (injectedNativeCall != null)
{ {
@ -347,9 +359,11 @@ namespace Grpc.Core.Internal
var credentials = details.Options.Credentials; var credentials = details.Options.Credentials;
using (var nativeCredentials = credentials != null ? credentials.ToNativeCredentials() : null) using (var nativeCredentials = credentials != null ? credentials.ToNativeCredentials() : null)
{ {
return details.Channel.Handle.CreateCall(environment.CompletionRegistry, var result = details.Channel.Handle.CreateCall(environment.CompletionRegistry,
parentCall, ContextPropagationToken.DefaultMask, cq, parentCall, ContextPropagationToken.DefaultMask, cq,
details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value), nativeCredentials); details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value), nativeCredentials);
return result;
}
} }
} }
@ -384,6 +398,8 @@ namespace Grpc.Core.Internal
/// Handler for unary response completion. /// Handler for unary response completion.
/// </summary> /// </summary>
private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders) private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders)
{
using (Profilers.ForCurrentThread().NewScope("AsyncCall.HandleUnaryResponse"))
{ {
TResponse msg = default(TResponse); TResponse msg = default(TResponse);
var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null; var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null;
@ -399,6 +415,7 @@ namespace Grpc.Core.Internal
finishedStatus = receivedStatus; finishedStatus = receivedStatus;
ReleaseResourcesIfPossible(); ReleaseResourcesIfPossible();
} }
responseHeadersTcs.SetResult(responseHeaders); responseHeadersTcs.SetResult(responseHeaders);
@ -413,6 +430,7 @@ namespace Grpc.Core.Internal
unaryResponseTcs.SetResult(msg); unaryResponseTcs.SetResult(msg);
} }
}
/// <summary> /// <summary>
/// Handles receive status completion for calls with streaming response. /// Handles receive status completion for calls with streaming response.

@ -41,6 +41,7 @@ using System.Threading.Tasks;
using Grpc.Core.Internal; using Grpc.Core.Internal;
using Grpc.Core.Logging; using Grpc.Core.Logging;
using Grpc.Core.Profiling;
using Grpc.Core.Utils; using Grpc.Core.Utils;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
@ -166,6 +167,8 @@ namespace Grpc.Core.Internal
/// the underlying native resources. /// the underlying native resources.
/// </summary> /// </summary>
protected bool ReleaseResourcesIfPossible() protected bool ReleaseResourcesIfPossible()
{
using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.ReleaseResourcesIfPossible"))
{ {
if (!disposed && call != null) if (!disposed && call != null)
{ {
@ -178,6 +181,7 @@ namespace Grpc.Core.Internal
} }
return false; return false;
} }
}
protected abstract bool IsClient protected abstract bool IsClient
{ {
@ -227,9 +231,12 @@ namespace Grpc.Core.Internal
} }
protected byte[] UnsafeSerialize(TWrite msg) protected byte[] UnsafeSerialize(TWrite msg)
{
using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.UnsafeSerialize"))
{ {
return serializer(msg); return serializer(msg);
} }
}
protected Exception TrySerialize(TWrite msg, out byte[] payload) protected Exception TrySerialize(TWrite msg, out byte[] payload)
{ {
@ -246,11 +253,15 @@ namespace Grpc.Core.Internal
} }
protected Exception TryDeserialize(byte[] payload, out TRead msg) protected Exception TryDeserialize(byte[] payload, out TRead msg)
{
using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.TryDeserialize"))
{ {
try try
{ {
msg = deserializer(payload); msg = deserializer(payload);
return null; return null;
} }
catch (Exception e) catch (Exception e)
{ {
@ -258,6 +269,7 @@ namespace Grpc.Core.Internal
return e; return e;
} }
} }
}
protected void FireCompletion<T>(AsyncCompletionDelegate<T> completionDelegate, T value, Exception error) protected void FireCompletion<T>(AsyncCompletionDelegate<T> completionDelegate, T value, Exception error)
{ {

@ -34,6 +34,7 @@ using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Grpc.Core; using Grpc.Core;
using Grpc.Core.Utils; using Grpc.Core.Utils;
using Grpc.Core.Profiling;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
{ {
@ -130,10 +131,13 @@ namespace Grpc.Core.Internal
} }
public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
{
using (Profilers.ForCurrentThread().NewScope("CallSafeHandle.StartUnary"))
{ {
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags) grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
.CheckOk(); .CheckOk();
} }
}
public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray) public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray)
{ {

@ -32,6 +32,7 @@ using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Grpc.Core.Profiling;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
{ {
@ -83,6 +84,8 @@ namespace Grpc.Core.Internal
} }
public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline, CredentialsSafeHandle credentials) public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline, CredentialsSafeHandle credentials)
{
using (Profilers.ForCurrentThread().NewScope("ChannelSafeHandle.CreateCall"))
{ {
var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline); var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline);
if (credentials != null) if (credentials != null)
@ -92,6 +95,7 @@ namespace Grpc.Core.Internal
result.SetCompletionRegistry(registry); result.SetCompletionRegistry(registry);
return result; return result;
} }
}
public ChannelState CheckConnectivityState(bool tryToConnect) public ChannelState CheckConnectivityState(bool tryToConnect)
{ {

@ -31,6 +31,7 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using Grpc.Core.Profiling;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
{ {
@ -69,9 +70,12 @@ namespace Grpc.Core.Internal
} }
public CompletionQueueEvent Pluck(IntPtr tag) public CompletionQueueEvent Pluck(IntPtr tag)
{
using (Profilers.ForCurrentThread().NewScope("CompletionQueueSafeHandle.Pluck"))
{ {
return grpcsharp_completion_queue_pluck(this, tag); return grpcsharp_completion_queue_pluck(this, tag);
} }
}
public void Shutdown() public void Shutdown()
{ {

@ -102,6 +102,9 @@ namespace Grpc.Core.Internal
/* Realtime clock */ /* Realtime clock */
Realtime, Realtime,
/* Precise clock good for performance profiling. */
Precise,
/* Timespan - the distance between two time points */ /* Timespan - the distance between two time points */
Timespan Timespan
} }

@ -31,6 +31,7 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using Grpc.Core.Profiling;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
{ {
@ -65,6 +66,8 @@ namespace Grpc.Core.Internal
} }
public static MetadataArraySafeHandle Create(Metadata metadata) public static MetadataArraySafeHandle Create(Metadata metadata)
{
using (Profilers.ForCurrentThread().NewScope("MetadataArraySafeHandle.Create"))
{ {
// TODO(jtattermusch): we might wanna check that the metadata is readonly // TODO(jtattermusch): we might wanna check that the metadata is readonly
var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count)); var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count));
@ -75,6 +78,7 @@ namespace Grpc.Core.Internal
} }
return metadataArray; return metadataArray;
} }
}
/// <summary> /// <summary>
/// Reads metadata from pointer to grpc_metadata_array /// Reads metadata from pointer to grpc_metadata_array

@ -239,6 +239,19 @@ namespace Grpc.Core.Internal
} }
} }
/// <summary>
/// Gets current timestamp using <c>GPRClockType.Precise</c>.
/// Only available internally because core needs to be compiled with
/// GRPC_TIMERS_RDTSC support for this to use RDTSC.
/// </summary>
internal static Timespec PreciseNow
{
get
{
return gprsharp_now(GPRClockType.Precise);
}
}
internal static int NativeSize internal static int NativeSize
{ {
get get

@ -0,0 +1,47 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.IO;
using System.Threading;
using Grpc.Core.Internal;
namespace Grpc.Core.Profiling
{
internal interface IProfiler
{
void Begin(string tag);
void End(string tag);
void Mark(string tag);
}
}

@ -0,0 +1,87 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.IO;
using System.Threading;
using Grpc.Core.Internal;
namespace Grpc.Core.Profiling
{
internal struct ProfilerEntry
{
public enum Type {
BEGIN,
END,
MARK
}
public ProfilerEntry(Timespec timespec, Type type, string tag)
{
this.timespec = timespec;
this.type = type;
this.tag = tag;
}
public Timespec timespec;
public Type type;
public string tag;
public override string ToString()
{
// mimic the output format used by C core.
return string.Format(
"{{\"t\": {0}.{1}, \"thd\":\"unknown\", \"type\": \"{2}\", \"tag\": \"{3}\", " +
"\"file\": \"unknown\", \"line\": 0, \"imp\": 0}}",
timespec.TimevalSeconds, timespec.TimevalNanos.ToString("D9"),
GetTypeAbbreviation(type), tag);
}
internal static string GetTypeAbbreviation(Type type)
{
switch (type)
{
case Type.BEGIN:
return "{";
case Type.END:
return "}";
case Type.MARK:
return ".";
default:
throw new ArgumentException("Unknown type");
}
}
}
}

@ -0,0 +1,60 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.IO;
using System.Threading;
using Grpc.Core.Internal;
namespace Grpc.Core.Profiling
{
// Allows declaring Begin and End of a profiler scope with a using statement.
// declared as struct for better performance.
internal struct ProfilerScope : IDisposable
{
readonly IProfiler profiler;
readonly string tag;
public ProfilerScope(IProfiler profiler, string tag)
{
this.profiler = profiler;
this.tag = tag;
this.profiler.Begin(this.tag);
}
public void Dispose()
{
profiler.End(tag);
}
}
}

@ -0,0 +1,131 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.IO;
using System.Threading;
using Grpc.Core.Internal;
namespace Grpc.Core.Profiling
{
internal static class Profilers
{
static readonly NopProfiler defaultProfiler = new NopProfiler();
static readonly ThreadLocal<IProfiler> profilers = new ThreadLocal<IProfiler>();
public static IProfiler ForCurrentThread()
{
return profilers.Value ?? defaultProfiler;
}
public static void SetForCurrentThread(IProfiler profiler)
{
profilers.Value = profiler;
}
public static ProfilerScope NewScope(this IProfiler profiler, string tag)
{
return new ProfilerScope(profiler, tag);
}
}
internal class NopProfiler : IProfiler
{
public void Begin(string tag)
{
}
public void End(string tag)
{
}
public void Mark(string tag)
{
}
}
// Profiler using Timespec.PreciseNow
internal class BasicProfiler : IProfiler
{
ProfilerEntry[] entries;
int count;
public BasicProfiler() : this(1024*1024)
{
}
public BasicProfiler(int capacity)
{
this.entries = new ProfilerEntry[capacity];
}
public void Begin(string tag) {
AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.BEGIN, tag));
}
public void End(string tag) {
AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.END, tag));
}
public void Mark(string tag) {
AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.MARK, tag));
}
public void Reset()
{
count = 0;
}
public void Dump(string filepath)
{
using (var stream = new StreamWriter(filepath))
{
Dump(stream);
}
}
public void Dump(TextWriter stream)
{
for (int i = 0; i < count; i++)
{
var entry = entries[i];
stream.WriteLine(entry.ToString());
}
}
// NOT THREADSAFE!
void AddEntry(ProfilerEntry entry) {
entries[count++] = entry;
}
}
}
Loading…
Cancel
Save