Last active
August 29, 2015 14:20
-
-
Save naveensrinivasan/b72fd80876eb67557ae8 to your computer and use it in GitHub Desktop.
MeasureIt With GC Information
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Query Kind="Program"> | |
<NuGetReference>MeasureIt.exe</NuGetReference> | |
<NuGetReference>Microsoft.Diagnostics.Tracing.TraceEvent</NuGetReference>a | |
<NuGetReference>Rx-Core</NuGetReference> | |
<NuGetReference>Rx-Interfaces</NuGetReference> | |
<NuGetReference>Rx-Linq</NuGetReference> | |
<NuGetReference>Rx-Main</NuGetReference> | |
<Namespace>Microsoft.Diagnostics.Tracing</Namespace> | |
<Namespace>Microsoft.Diagnostics.Tracing.Parsers</Namespace> | |
<Namespace>Microsoft.Diagnostics.Tracing.Parsers.Clr</Namespace> | |
<Namespace>Microsoft.Diagnostics.Tracing.Session</Namespace> | |
<Namespace>System</Namespace> | |
<Namespace>System.Collections.Generic</Namespace> | |
<Namespace>System.Diagnostics</Namespace> | |
<Namespace>System.Diagnostics.Tracing</Namespace> | |
<Namespace>System.IO</Namespace> | |
<Namespace>System.Reactive.Linq</Namespace> | |
<Namespace>System.Threading</Namespace> | |
<Namespace>System.Threading.Tasks</Namespace> | |
<Namespace>PerformanceMeasurement</Namespace> | |
<Namespace>System.Reactive.Subjects</Namespace> | |
</Query> | |
void Main() | |
{ | |
/* | |
Measure GC Allocations and Duration it take to run a piece of code. | |
It uses MeasureIt https://www.nuget.org/packages/MeasureIt.exe/ along with | |
TraceEvent https://www.nuget.org/packages/Microsoft.Diagnostics.Tracing.TraceEvent/ | |
for figuring out GC Allocations. | |
*/ | |
var useAny = true; | |
var loremipsum = @"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. | |
Aenean commodo ligula eget dolor. Aenean massa. | |
Cum sociis natoque penatibus et magnis dis parturient montes, | |
nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, | |
pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla | |
vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, | |
venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer | |
tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend | |
tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. | |
Aliquam lorem ante, dapibus in, viverra quis, feugiat a,"; | |
var items = loremipsum.Split(new [] {' '}).Distinct().ToList(); | |
var lookfor = "pede"; | |
//Function that needs to be measured. | |
var runPerf =new Action(() => { | |
if (!useAny) // Based on the flag run either of the code. | |
{ | |
foreach (var item in items) | |
{ | |
if (item == lookfor) { | |
return; | |
} | |
} | |
} | |
else | |
{ | |
items.Any (i => i == lookfor); | |
} | |
}); | |
LinqPadUX.Measure.ActionWithGC(runPerf,500); | |
} | |
public static class ObserveGCEvents | |
{ | |
// Filter because the traceevent reports for every .NET application running. | |
const string FilterProcessName = "LINQPad.UserQuery"; | |
public static void ActionWithGC(this FluentMeasure measure, | |
Action runPerf,int counter = 1000) | |
{ | |
if (TraceEventSession.IsElevated() != true) | |
{ | |
throw new | |
Exception(@"You have the run this process as | |
elevated to capture traces."); | |
} | |
// create a real time user mode session for capturing ETW Traces | |
var userSession = new TraceEventSession("ObserveGCAllocs"); | |
//Subscriber for GC ETW Events | |
userSession.EnableProvider( | |
ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, | |
(ulong)(ClrTraceEventParser.Keywords.GC)); | |
//Observable for GC Allocation | |
IObservable<GCAllocationTickTraceData> gcAllocStream = | |
userSession.Source.Clr.Observe<GCAllocationTickTraceData>(); | |
//Observable for GC Collection | |
IObservable<GCHeapStatsTraceData> gcCollectStream | |
= userSession.Source.Clr.Observe<GCHeapStatsTraceData>(); | |
var alloc = gcAllocStream | |
.Select(data => new {ProcessName = GetProcessName(data.ProcessID), | |
Amount = data.AllocationAmount / 1000, | |
Type = data.TypeName, | |
AllocationKind = data.AllocationKind}) | |
/* Filter*/ .Where(item => item.ProcessName.Contains(FilterProcessName)) | |
.GroupBy(item => item.Type) | |
.Select(item => new {Type = item.Key, | |
AllocationAmount = | |
item.Select(x => x.Amount).Average() , | |
Count = item.Count().FirstAsync() | |
}).Dump("GC Allocation"); | |
gcCollectStream.Select(data => | |
new CollectionStats {ProcessName = GetProcessName(data.ProcessID), | |
Gen0 = data.GenerationSize0 / 1000000.0, | |
Gen1 = data.GenerationSize1 / 1000000.0, | |
Gen2 = data.GenerationSize2 / 1000000.0, | |
Gen3 = data.GenerationSize3 / 1000000.0, | |
GCHandleCount = data.GCHandleCount, | |
PinnedObjectCount = data.PinnedObjectCount}) | |
.Where(item => item | |
.ProcessName.Contains(FilterProcessName)) | |
.Dump("GC Collection Stats"); | |
/* Starts Processing ETW Events */ | |
Task.Run(() => userSession.Source.Process()); | |
Task.Run( () =>LinqPadUX.Measure | |
.WithIterationCount(counter) | |
/* Run's the function */ .Action(runPerf).Dump()) | |
.ContinueWith((t) => | |
/* Stops the Trace session */ userSession.Stop()); | |
} | |
private static string GetProcessName(int processID) | |
{ | |
// Only keep the cache for 10 seconds to avoid issues with process ID reuse. | |
var now = DateTime.UtcNow; | |
if ((now - s_processNameCacheLastUpdate).TotalSeconds > 10) | |
s_processNameCache.Clear(); | |
s_processNameCacheLastUpdate = now; | |
string ret = null; | |
if (!s_processNameCache.TryGetValue(processID, out ret)) | |
{ | |
Process proc = null; | |
try { proc = Process.GetProcessById(processID); } | |
catch (Exception) { } | |
if (proc != null) | |
ret = proc.ProcessName; | |
if (string.IsNullOrWhiteSpace(ret)) | |
ret = processID.ToString(); | |
s_processNameCache.Add(processID, ret); | |
} | |
return ret; | |
} | |
private static Dictionary<int, string> s_processNameCache = | |
new Dictionary<int, string>(); | |
private static DateTime s_processNameCacheLastUpdate; | |
} | |
class CollectionStats | |
{ | |
public string ProcessName { get; set; } | |
public double Gen0 { get; set; } | |
public double Gen1 { get; set; } | |
public double Gen2 { get; set; } | |
public double Gen3 { get; set; } | |
public int GCHandleCount { get; set; } | |
public int PinnedObjectCount { get; set; } | |
} | |
class AllocationData | |
{ | |
public string Type { get; set; } | |
public IObservable<int> ItemSize { get; set; } | |
public IObservable<int> Count { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment