Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stats per function call to Function Chart (GUI) #139

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions gui/Optick/Views/FunctionDescriptionView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,20 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Width="16" Height="16" VerticalAlignment="Center" StrokeThickness="1" Stroke="Black" Fill="{Binding Stats.Description.ForceBrush}" Margin="0,0,3,0" />
<TextBlock Grid.Column="1" Text="{Binding Stats.Description}" FontWeight="Bold" />
<StackPanel Grid.Column="2" Orientation="Horizontal" TextBlock.FontWeight="Bold">
<TextBlock Text="{Binding Stats.AvgTotal, StringFormat={}{0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" ToolTip="Average Time" />
<TextBlock Text="{Binding Stats.AvgWork, StringFormat={}{0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" Foreground="LimeGreen" ToolTip="Average Work Time" />
<TextBlock Text="{Binding Stats.AvgWait, StringFormat={}{0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" Foreground="Tomato" ToolTip="Average Wait Time" />
<TextBlock Grid.Column="1" Text="{Binding Stats.Description}" FontWeight="Bold" VerticalAlignment="Center" />
<StackPanel Grid.Column="2" Orientation="Vertical">
<StackPanel Orientation="Horizontal" TextBlock.FontWeight="Bold" HorizontalAlignment="Right">
<TextBlock Text="{Binding Stats.AvgTotal, StringFormat={}{0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" ToolTip="Time spent in this function during an average frame" />
<TextBlock Text="{Binding Stats.AvgWork, StringFormat={}{0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" Foreground="LimeGreen" ToolTip="Time spent working in this function during an average frame" />
<TextBlock Text="{Binding Stats.AvgWait, StringFormat={}{0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" Foreground="Tomato" ToolTip="Time spent waiting in this function during an average frame" />
</StackPanel>
<StackPanel Orientation="Horizontal" TextBlock.FontWeight="Medium" HorizontalAlignment="Right">
<TextBlock Text="{Binding Stats.MinPerCall, StringFormat={}Min {0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" Foreground="LightGray" ToolTip="Fastest Time (single call)" />
<TextBlock Text="{Binding Stats.MaxPerCall, StringFormat={}Max {0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" Foreground="LightGray" ToolTip="Slowest Time (single call)" />
<TextBlock Text="{Binding Stats.AvgTotalPerCall, StringFormat={}Avg {0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" Foreground="LightBlue" ToolTip="Average Time (single call)" />
<TextBlock Text="{Binding Stats.StdDevPerCall, StringFormat={}SD {0:0.000}ms, Mode=OneWay}" Margin="6,0,0,0" Foreground="BurlyWood" ToolTip="Standard Deviation (single call)" />
</StackPanel>
</StackPanel>

</Grid>
</UserControl>
159 changes: 104 additions & 55 deletions gui/Profiler.Data/FunctionStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,25 @@ public enum Origin
public FrameGroup Group { get; set; }
public EventDescription Description { get; set; }

public double AvgTotal
{
get { return Samples.Count > 0 ? Samples.Average(s => s.Total) : 0.0; }
}

public double AvgWork
{
get { return Samples.Count > 0 ? Samples.Average(s => s.Work) : 0.0; }
}

public double AvgWait
{
get { return Samples.Count > 0 ? Samples.Average(s => s.Wait) : 0.0; }
}


public FunctionStats(FrameGroup group, EventDescription desc)
// Timing stats computed during load

// Fastest time
public double MinPerCall { get; set; }
// Slowest time
public double MaxPerCall { get; set; }
// Average function time (averaged over calls, not frames)
public double AvgTotalPerCall { get; set; }
// Standard deviation of the individual function times
public double StdDevPerCall { get; set; }

// Time spent in this function during an average frame
public double AvgTotal { get; set; }
// Time spent working in this function during an average frame
public double AvgWork { get; set; }
// Time spent waiting in this function during an average frame
public double AvgWait { get; set; }

public FunctionStats(FrameGroup group, EventDescription desc)
{
Group = group;
Description = desc;
Expand All @@ -73,69 +75,116 @@ public void Load(Origin origin = Origin.MainThread)
{
Samples = new List<Sample>();

MinPerCall = Double.MaxValue;
MaxPerCall = 0.0;
AvgTotalPerCall = 0.0;
StdDevPerCall = 0.0;
double sumOfCallTimes = 0.0;
double sumOfCallTimesSq = 0.0;
double sumOfFrameTimes = 0.0;
double sumOfFrameTimesWork = 0.0;
double sumOfFrameTimesWait = 0.0;
int numCalls = 0;

if (origin == Origin.MainThread)
{
Group.UpdateDescriptionMask(Description);

if (Description.Mask != null)
if (Description.Mask == null)
{
FrameList frameList = Group.GetFocusThread(Description.Mask.Value);
if (frameList != null)
{
List<FrameData> frames = frameList.Events;
return;
}

for (int i = 0; i < frames.Count; ++i)
{
Sample sample = new Sample() { Name = String.Format("Frame {0:000}", i), Index = i };
FrameList frameList = Group.GetFocusThread(Description.Mask.Value);
if (frameList == null)
{
// Fallback to Individual Calls
Load(Origin.IndividualCalls);
return;
}

List<FrameData> frames = frameList.Events;

long start = frames[i].Start;
long finish = i == frames.Count - 1 ? frames[i].Finish : frames[i + 1].Start;
for (int i = 0; i < frames.Count; ++i)
{
Sample sample = new Sample() { Name = String.Format("Frame {0:000}", i), Index = i };

foreach (ThreadData thread in Group.Threads)
long start = frames[i].Start;
long finish = i == frames.Count - 1 ? frames[i].Finish : frames[i + 1].Start;

foreach (ThreadData thread in Group.Threads)
{
Utils.ForEachInsideIntervalStrict(thread.Events, start, finish, (frame) =>
{
List<Entry> shortEntries = null;
if (frame.ShortBoard.TryGetValue(Description, out shortEntries))
{
Utils.ForEachInsideIntervalStrict(thread.Events, start, finish, (frame) =>
foreach (Entry e in shortEntries)
{
List<Entry> shortEntries = null;
if (frame.ShortBoard.TryGetValue(Description, out shortEntries))
{
foreach (Entry e in shortEntries)
{
sample.Add(e);
}
}
});
double dur = e.Duration;
sumOfCallTimes += dur;
sumOfCallTimesSq += dur * dur;
numCalls++;
MinPerCall = Math.Min(MinPerCall, dur);
MaxPerCall = Math.Max(MaxPerCall, dur);

sample.Add(e);
}
}

Samples.Add(sample);
}
});
}
else
{
// Fallback to Individual Calls
Load(Origin.IndividualCalls);
}
}
}

if (origin == Origin.IndividualCalls)
sumOfFrameTimes += sample.Total;
sumOfFrameTimesWait += sample.Wait;
sumOfFrameTimesWork += sample.Work;
Samples.Add(sample);
}
}
else if (origin == Origin.IndividualCalls)
{
foreach (ThreadData thread in Group.Threads)
{
foreach (EventFrame frame in thread.Events)
{
List<Entry> shortEntries = null;
if (frame.ShortBoard.TryGetValue(Description, out shortEntries))
if (!frame.ShortBoard.TryGetValue(Description, out shortEntries))
{
foreach (Entry e in shortEntries)
{
Samples.Add(new Sample(e) { Index = Samples.Count, Name = Description.Name });
}
continue;
}

foreach (Entry e in shortEntries)
{
double dur = e.Duration;
sumOfCallTimes += dur;
sumOfCallTimesSq += dur * dur;
numCalls++;
MinPerCall = Math.Min(MinPerCall, dur);
MaxPerCall = Math.Max(MaxPerCall, dur);

Sample sample = new Sample(e) { Index = Samples.Count, Name = Description.Name };

sumOfFrameTimes += sample.Total;
sumOfFrameTimesWait += sample.Wait;
sumOfFrameTimesWork += sample.Work;
Samples.Add(sample);
}
}
}

Samples.Sort((a, b) => (a.Entries[0].CompareTo(b.Entries[0])));
Samples.Sort((a, b) => a.Entries[0].CompareTo(b.Entries[0]));
}

// compute averages
double numCallsInv = numCalls > 0 ? 1.0 / numCalls : 0.0;
double numSamplesInv = Samples.Count > 0 ? 1.0 / Samples.Count : 0.0;

AvgTotalPerCall = sumOfCallTimes * numCallsInv;
double varOfTimes = sumOfCallTimesSq * numCallsInv - AvgTotalPerCall * AvgTotalPerCall;
StdDevPerCall = Math.Sqrt(Math.Max(varOfTimes, 0.0));

AvgTotal = numSamplesInv * sumOfFrameTimes;
AvgWait = numSamplesInv * sumOfFrameTimesWait;
AvgWork = numSamplesInv * sumOfFrameTimesWork;
}
}
}