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. 106
      src/csharp/Grpc.Core/Internal/AsyncCall.cs
  7. 42
      src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
  8. 8
      src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
  9. 14
      src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
  10. 6
      src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
  11. 3
      src/csharp/Grpc.Core/Internal/Enums.cs
  12. 16
      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;
@ -200,19 +201,6 @@ namespace Grpc.Core.Tests
Assert.AreEqual(headers[1].Key, trailers[1].Key); Assert.AreEqual(headers[1].Key, trailers[1].Key);
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,24 +108,26 @@ 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.
return unaryResponseTcs.Task.GetAwaiter().GetResult(); return unaryResponseTcs.Task.GetAwaiter().GetResult();
@ -329,27 +335,35 @@ namespace Grpc.Core.Internal
private void Initialize(CompletionQueueSafeHandle cq) private void Initialize(CompletionQueueSafeHandle cq)
{ {
var call = CreateNativeCall(cq); using (Profilers.ForCurrentThread().NewScope("AsyncCall.Initialize"))
details.Channel.AddCallReference(this); {
InitializeInternal(call); var call = CreateNativeCall(cq);
RegisterCancellationCallback();
details.Channel.AddCallReference(this);
InitializeInternal(call);
RegisterCancellationCallback();
}
} }
private INativeCall CreateNativeCall(CompletionQueueSafeHandle cq) private INativeCall CreateNativeCall(CompletionQueueSafeHandle cq)
{ {
if (injectedNativeCall != null) using (Profilers.ForCurrentThread().NewScope("AsyncCall.CreateNativeCall"))
{ {
return injectedNativeCall; // allows injecting a mock INativeCall in tests. if (injectedNativeCall != null)
} {
return injectedNativeCall; // allows injecting a mock INativeCall in tests.
}
var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance; var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance;
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;
}
} }
} }
@ -385,33 +399,37 @@ namespace Grpc.Core.Internal
/// </summary> /// </summary>
private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders) private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders)
{ {
TResponse msg = default(TResponse); using (Profilers.ForCurrentThread().NewScope("AsyncCall.HandleUnaryResponse"))
var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null;
lock (myLock)
{ {
finished = true; TResponse msg = default(TResponse);
var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null;
if (deserializeException != null && receivedStatus.Status.StatusCode == StatusCode.OK) lock (myLock)
{ {
receivedStatus = new ClientSideStatus(DeserializeResponseFailureStatus, receivedStatus.Trailers); finished = true;
if (deserializeException != null && receivedStatus.Status.StatusCode == StatusCode.OK)
{
receivedStatus = new ClientSideStatus(DeserializeResponseFailureStatus, receivedStatus.Trailers);
}
finishedStatus = receivedStatus;
ReleaseResourcesIfPossible();
} }
finishedStatus = receivedStatus;
ReleaseResourcesIfPossible(); responseHeadersTcs.SetResult(responseHeaders);
}
responseHeadersTcs.SetResult(responseHeaders); var status = receivedStatus.Status;
var status = receivedStatus.Status; if (!success || status.StatusCode != StatusCode.OK)
{
unaryResponseTcs.SetException(new RpcException(status));
return;
}
if (!success || status.StatusCode != StatusCode.OK) unaryResponseTcs.SetResult(msg);
{
unaryResponseTcs.SetException(new RpcException(status));
return;
} }
unaryResponseTcs.SetResult(msg);
} }
/// <summary> /// <summary>

@ -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
@ -167,16 +168,19 @@ namespace Grpc.Core.Internal
/// </summary> /// </summary>
protected bool ReleaseResourcesIfPossible() protected bool ReleaseResourcesIfPossible()
{ {
if (!disposed && call != null) using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.ReleaseResourcesIfPossible"))
{ {
bool noMoreSendCompletions = sendCompletionDelegate == null && (halfcloseRequested || cancelRequested || finished); if (!disposed && call != null)
if (noMoreSendCompletions && readingDone && finished)
{ {
ReleaseResources(); bool noMoreSendCompletions = sendCompletionDelegate == null && (halfcloseRequested || cancelRequested || finished);
return true; if (noMoreSendCompletions && readingDone && finished)
{
ReleaseResources();
return true;
}
} }
return false;
} }
return false;
} }
protected abstract bool IsClient protected abstract bool IsClient
@ -228,7 +232,10 @@ namespace Grpc.Core.Internal
protected byte[] UnsafeSerialize(TWrite msg) protected byte[] UnsafeSerialize(TWrite msg)
{ {
return serializer(msg); using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.UnsafeSerialize"))
{
return serializer(msg);
}
} }
protected Exception TrySerialize(TWrite msg, out byte[] payload) protected Exception TrySerialize(TWrite msg, out byte[] payload)
@ -247,15 +254,20 @@ namespace Grpc.Core.Internal
protected Exception TryDeserialize(byte[] payload, out TRead msg) protected Exception TryDeserialize(byte[] payload, out TRead msg)
{ {
try using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.TryDeserialize"))
{
msg = deserializer(payload);
return null;
}
catch (Exception e)
{ {
msg = default(TRead); try
return e; {
msg = deserializer(payload);
return null;
}
catch (Exception e)
{
msg = default(TRead);
return e;
}
} }
} }

@ -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
{ {
@ -131,8 +132,11 @@ 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)
{ {
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags) using (Profilers.ForCurrentThread().NewScope("CallSafeHandle.StartUnary"))
.CheckOk(); {
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
.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
{ {
@ -84,13 +85,16 @@ 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)
{ {
var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline); using (Profilers.ForCurrentThread().NewScope("ChannelSafeHandle.CreateCall"))
if (credentials != null)
{ {
result.SetCredentials(credentials); var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline);
if (credentials != null)
{
result.SetCredentials(credentials);
}
result.SetCompletionRegistry(registry);
return result;
} }
result.SetCompletionRegistry(registry);
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
{ {
@ -70,7 +71,10 @@ namespace Grpc.Core.Internal
public CompletionQueueEvent Pluck(IntPtr tag) public CompletionQueueEvent Pluck(IntPtr tag)
{ {
return grpcsharp_completion_queue_pluck(this, tag); using (Profilers.ForCurrentThread().NewScope("CompletionQueueSafeHandle.Pluck"))
{
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
{ {
@ -66,14 +67,17 @@ namespace Grpc.Core.Internal
public static MetadataArraySafeHandle Create(Metadata metadata) public static MetadataArraySafeHandle Create(Metadata metadata)
{ {
// TODO(jtattermusch): we might wanna check that the metadata is readonly using (Profilers.ForCurrentThread().NewScope("MetadataArraySafeHandle.Create"))
var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count));
for (int i = 0; i < metadata.Count; i++)
{ {
var valueBytes = metadata[i].GetSerializedValueUnsafe(); // TODO(jtattermusch): we might wanna check that the metadata is readonly
grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, valueBytes, new UIntPtr((ulong)valueBytes.Length)); var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count));
for (int i = 0; i < metadata.Count; i++)
{
var valueBytes = metadata[i].GetSerializedValueUnsafe();
grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, valueBytes, new UIntPtr((ulong)valueBytes.Length));
}
return metadataArray;
} }
return metadataArray;
} }
/// <summary> /// <summary>

@ -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