Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
MeasureIt With GC Information
<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
You can’t perform that action at this time.