Enhance Profiler with memory allocation metrics
This commit is contained in:
@@ -39,13 +39,15 @@ public static class Profiler
|
||||
|
||||
public readonly struct LabelMetrics
|
||||
{
|
||||
public LabelMetrics(double inclusiveMs, double selfMs, uint calls, double minInclusiveMs, double maxInclusiveMs)
|
||||
public LabelMetrics(double inclusiveMs, double selfMs, uint calls, double minInclusiveMs, double maxInclusiveMs, long allocatedBytes, long selfAllocatedBytes)
|
||||
{
|
||||
InclusiveMs = inclusiveMs;
|
||||
SelfMs = selfMs;
|
||||
Calls = calls;
|
||||
MinInclusiveMs = minInclusiveMs;
|
||||
MaxInclusiveMs = maxInclusiveMs;
|
||||
AllocatedBytes = allocatedBytes;
|
||||
SelfAllocatedBytes = selfAllocatedBytes;
|
||||
}
|
||||
|
||||
public double InclusiveMs { get; }
|
||||
@@ -53,6 +55,8 @@ public static class Profiler
|
||||
public uint Calls { get; }
|
||||
public double MinInclusiveMs { get; }
|
||||
public double MaxInclusiveMs { get; }
|
||||
public long AllocatedBytes { get; }
|
||||
public long SelfAllocatedBytes { get; }
|
||||
}
|
||||
|
||||
public readonly struct RollingLabelMetrics
|
||||
@@ -340,6 +344,8 @@ public static class Profiler
|
||||
{
|
||||
double inclusiveMs = node.ElapsedMilliseconds();
|
||||
double selfMs = node.SelfMilliseconds();
|
||||
long allocBytes = node.AllocatedBytes;
|
||||
long selfAllocBytes = node.SelfAllocatedBytes();
|
||||
|
||||
if (labelMetrics.TryGetValue(node.Label, out LabelMetrics current))
|
||||
{
|
||||
@@ -348,11 +354,13 @@ public static class Profiler
|
||||
current.SelfMs + selfMs,
|
||||
current.Calls + 1,
|
||||
Math.Min(current.MinInclusiveMs, inclusiveMs),
|
||||
Math.Max(current.MaxInclusiveMs, inclusiveMs));
|
||||
Math.Max(current.MaxInclusiveMs, inclusiveMs),
|
||||
current.AllocatedBytes + allocBytes,
|
||||
current.SelfAllocatedBytes + selfAllocBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
labelMetrics[node.Label] = new LabelMetrics(inclusiveMs, selfMs, 1, inclusiveMs, inclusiveMs);
|
||||
labelMetrics[node.Label] = new LabelMetrics(inclusiveMs, selfMs, 1, inclusiveMs, inclusiveMs, allocBytes, selfAllocBytes);
|
||||
}
|
||||
|
||||
if (categoryMetrics.TryGetValue(node.Category, out LabelMetrics categoryCurrent))
|
||||
@@ -362,11 +370,13 @@ public static class Profiler
|
||||
categoryCurrent.SelfMs + selfMs,
|
||||
categoryCurrent.Calls + 1,
|
||||
Math.Min(categoryCurrent.MinInclusiveMs, inclusiveMs),
|
||||
Math.Max(categoryCurrent.MaxInclusiveMs, inclusiveMs));
|
||||
Math.Max(categoryCurrent.MaxInclusiveMs, inclusiveMs),
|
||||
categoryCurrent.AllocatedBytes + allocBytes,
|
||||
categoryCurrent.SelfAllocatedBytes + selfAllocBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
categoryMetrics[node.Category] = new LabelMetrics(inclusiveMs, selfMs, 1, inclusiveMs, inclusiveMs);
|
||||
categoryMetrics[node.Category] = new LabelMetrics(inclusiveMs, selfMs, 1, inclusiveMs, inclusiveMs, allocBytes, selfAllocBytes);
|
||||
}
|
||||
|
||||
for (int i = 0; i < node.Children.Count; i++)
|
||||
@@ -423,8 +433,11 @@ public static class Profiler
|
||||
public long EndTime { get; private set; }
|
||||
public int ManagedThreadId { get; private set; }
|
||||
public List<ScopeNode> Children { get; } = new List<ScopeNode>();
|
||||
public long AllocatedBytes { get; private set; }
|
||||
internal ScopeNode Parent { get; private set; }
|
||||
internal long ChildrenDurationTicks { get; private set; }
|
||||
internal long ChildrenAllocatedBytes { get; private set; }
|
||||
private long allocatedBytesAtStart;
|
||||
|
||||
internal void Reset(string label, string category, ulong tagMask, int managedThreadId, ScopeNode parent)
|
||||
{
|
||||
@@ -436,7 +449,10 @@ public static class Profiler
|
||||
StartTime = Stopwatch.GetTimestamp();
|
||||
EndTime = 0;
|
||||
ChildrenDurationTicks = 0;
|
||||
ChildrenAllocatedBytes = 0;
|
||||
AllocatedBytes = 0;
|
||||
Children.Clear();
|
||||
allocatedBytesAtStart = GC.GetAllocatedBytesForCurrentThread();
|
||||
}
|
||||
|
||||
internal void End()
|
||||
@@ -447,9 +463,11 @@ public static class Profiler
|
||||
}
|
||||
|
||||
EndTime = Stopwatch.GetTimestamp();
|
||||
AllocatedBytes = Math.Max(0, GC.GetAllocatedBytesForCurrentThread() - allocatedBytesAtStart);
|
||||
if (Parent != null)
|
||||
{
|
||||
Parent.ChildrenDurationTicks += Math.Max(0, EndTime - StartTime);
|
||||
Parent.ChildrenAllocatedBytes += AllocatedBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,6 +483,11 @@ public static class Profiler
|
||||
return ((double)selfTicks) * 1000 / Stopwatch.Frequency;
|
||||
}
|
||||
|
||||
public long SelfAllocatedBytes()
|
||||
{
|
||||
return Math.Max(0, AllocatedBytes - ChildrenAllocatedBytes);
|
||||
}
|
||||
|
||||
internal ScopeNode AddChild(string label, string category, ulong tagMask)
|
||||
{
|
||||
ScopeNode child = RentNode(label, category, tagMask, ManagedThreadId, this);
|
||||
|
||||
Reference in New Issue
Block a user