Skip to content
This repository has been archived by the owner on Jul 16, 2023. It is now read-only.

Commit

Permalink
Adding the TimingGroup class which is used to identify to which group…
Browse files Browse the repository at this point in the history
… a specific timing belongs. This allows separating timings based on their logical area or grouping.

references issue #2
  • Loading branch information
pvandervelde committed Dec 4, 2013
1 parent b3fd4e8 commit f2c9e5d
Show file tree
Hide file tree
Showing 20 changed files with 1,017 additions and 216 deletions.
6 changes: 6 additions & 0 deletions src/nuclei.diagnostics/Nuclei.Diagnostics.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@
<Compile Include="Profiling\CanOnlyMeasureIntervalOnceException.cs" />
<Compile Include="Profiling\IGenerateTimingReports.cs" />
<Compile Include="Profiling\IIntervalOwner.cs" />
<Compile Include="Profiling\NonMatchingTimingGroupsException.cs" />
<Compile Include="Profiling\TimingGroup.cs" />
<Compile Include="Profiling\IStoreIntervals.cs" />
<Compile Include="Profiling\ITimerInterval.cs" />
<Compile Include="Profiling\Profiler.cs" />
Expand Down Expand Up @@ -139,6 +141,10 @@
<Project>{62e77b2f-34ee-4258-b1c7-9f30eb80ae12}</Project>
<Name>Nuclei.Configuration</Name>
</ProjectReference>
<ProjectReference Include="..\Nuclei\Nuclei.csproj">
<Project>{7c12c611-1780-4e93-9b0e-b806b738559d}</Project>
<Name>Nuclei</Name>
</ProjectReference>
<ProjectReference Include="..\solutionlevel\SolutionLevel.csproj">
<Project>{b0b1c32a-53fc-4b7e-ba60-6dafb8180a42}</Project>
<Name>SolutionLevel</Name>
Expand Down
8 changes: 0 additions & 8 deletions src/nuclei.diagnostics/Profiling/IGenerateTimingReports.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@ public interface IGenerateTimingReports
/// <returns>A new report that contains all known timing intervals.</returns>
TimingReport FromStartTillEnd();

/// <summary>
/// Creates a new report that contains all timing intervals starting at the
/// specified interval till the current point in time.
/// </summary>
/// <param name="interval">The interval from which the report should start.</param>
/// <returns>A new report that contains all intervals since the specified interval.</returns>
TimingReport FromIntervalTillEnd(ITimerInterval interval);

/// <summary>
/// Creates a new report that contains all timing intervals starting at the specified
/// <paramref name="start"/> interval up to the <paramref name="inclusiveEnd"/> interval,
Expand Down
8 changes: 8 additions & 0 deletions src/nuclei.diagnostics/Profiling/ITimerInterval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ namespace Nuclei.Diagnostics.Profiling
/// </summary>
public interface ITimerInterval : IDisposable
{
/// <summary>
/// Gets the group to which the current interval belongs.
/// </summary>
TimingGroup Group
{
get;
}

/// <summary>
/// Gets the description for the current interval.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//-----------------------------------------------------------------------
// <copyright company="Nuclei">
// Copyright 2013 Nuclei. Licensed under the Apache License, Version 2.0.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Runtime.Serialization;
using Nuclei.Diagnostics.Properties;

namespace Nuclei.Diagnostics.Profiling
{
/// <summary>
/// An exception thrown when the user tries to use two or more <see cref="ITimerInterval"/>
/// objects with different <see cref="TimingGroup"/> instances.
/// </summary>
[Serializable]
public sealed class NonMatchingTimingGroupsException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="NonMatchingTimingGroupsException"/> class.
/// </summary>
public NonMatchingTimingGroupsException()
: this(Resources.Exceptions_Messages_NonMatchingTimingGroups)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="NonMatchingTimingGroupsException"/> class.
/// </summary>
/// <param name="message">The message.</param>
public NonMatchingTimingGroupsException(string message)
: base(message)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="NonMatchingTimingGroupsException"/> class.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="innerException">The inner exception.</param>
public NonMatchingTimingGroupsException(string message, Exception innerException)
: base(message, innerException)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="NonMatchingTimingGroupsException"/> class.
/// </summary>
/// <param name="info">
/// The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object
/// data about the exception being thrown.
/// </param>
/// <param name="context">
/// The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information
/// about the source or destination.
/// </param>
/// <exception cref="T:System.ArgumentNullException">
/// The <paramref name="info"/> parameter is null.
/// </exception>
/// <exception cref="T:System.Runtime.Serialization.SerializationException">
/// The class name is null or <see cref="P:System.Exception.HResult"/> is zero (0).
/// </exception>
private NonMatchingTimingGroupsException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}
36 changes: 28 additions & 8 deletions src/nuclei.diagnostics/Profiling/Profiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public sealed class Profiler : IIntervalOwner
/// <summary>
/// The currently active timers.
/// </summary>
private readonly Stack<ITimerInterval> m_ActiveIntervals = new Stack<ITimerInterval>();
private readonly IDictionary<TimingGroup, Stack<ITimerInterval>> m_ActiveIntervals
= new Dictionary<TimingGroup, Stack<ITimerInterval>>();

/// <summary>
/// The stopwatch that is used to track the time. Each timing interval will use
Expand Down Expand Up @@ -50,6 +51,7 @@ public Profiler(IStoreIntervals storage)
/// <summary>
/// Starts the measurement of a time interval.
/// </summary>
/// <param name="group">The group to which the current timing belongs.</param>
/// <param name="stepDescription">The description for the new interval.</param>
/// <returns>
/// The object that describes the interval.
Expand All @@ -62,14 +64,14 @@ public Profiler(IStoreIntervals storage)
/// }
/// </code>
/// </example>
internal ITimerInterval MeasureInterval(string stepDescription)
internal ITimerInterval MeasureInterval(TimingGroup group, string stepDescription)
{
if (!m_Timer.IsRunning)
{
m_Timer.Start();
}

var interval = new TimerInterval(this, stepDescription);
var interval = new TimerInterval(this, group, stepDescription);
StoreIntervalInTree(interval);

interval.Start();
Expand All @@ -78,17 +80,25 @@ internal ITimerInterval MeasureInterval(string stepDescription)

private void StoreIntervalInTree(TimerInterval interval)
{
if (m_ActiveIntervals.Count > 0)
Stack<ITimerInterval> stack;
if (m_ActiveIntervals.ContainsKey(interval.Group) && (m_ActiveIntervals[interval.Group].Count > 0))
{
var parent = m_ActiveIntervals.Peek();
stack = m_ActiveIntervals[interval.Group];
var parent = stack.Peek();
m_Storage.AddChildInterval(parent, interval);
}
else
{
if (!m_ActiveIntervals.ContainsKey(interval.Group))
{
m_ActiveIntervals.Add(interval.Group, new Stack<ITimerInterval>());
}

stack = m_ActiveIntervals[interval.Group];
m_Storage.AddBaseInterval(interval);
}

m_ActiveIntervals.Push(interval);
stack.Push(interval);
}

/// <summary>
Expand All @@ -110,12 +120,22 @@ public long CurrentTicks
public void StopInterval(ITimerInterval timerInterval)
{
{
Debug.Assert(timerInterval != null, "The interval that should be stopped should not be a null reference.");
Debug.Assert(
m_ActiveIntervals.ContainsKey(timerInterval.Group),
"The timer interval does not belong to a known timing group.");
Debug.Assert(
ReferenceEquals(m_ActiveIntervals.Peek(), timerInterval),
ReferenceEquals(m_ActiveIntervals[timerInterval.Group].Peek(), timerInterval),
"Parent interval stopped before child intervals have stopped.");
}

m_ActiveIntervals.Pop();
var stack = m_ActiveIntervals[timerInterval.Group];
stack.Pop();
if (stack.Count == 0)
{
m_ActiveIntervals.Remove(timerInterval.Group);
}

if (m_ActiveIntervals.Count == 0)
{
m_Timer.Stop();
Expand Down
5 changes: 3 additions & 2 deletions src/nuclei.diagnostics/Profiling/ProfilerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static class ProfilerExtensions
/// Returns an <see cref="ITimerInterval"/> object reference.
/// </summary>
/// <param name="profiler">The profiler which should provide the timer interval. May be <see langword="null" />.</param>
/// <param name="group">The group to which the current timing will belong.</param>
/// <param name="description">The description for the interval.</param>
/// <returns>
/// A new <see cref="ITimerInterval"/> if the <paramref name="profiler"/> reference is not <see langword="null" />;
Expand All @@ -31,9 +32,9 @@ public static class ProfilerExtensions
/// }
/// </code>
/// </example>
public static IDisposable Measure(this Profiler profiler, string description)
public static IDisposable Measure(this Profiler profiler, TimingGroup group, string description)
{
return (profiler != null) ? profiler.MeasureInterval(description) : null;
return (profiler != null) ? profiler.MeasureInterval(group, description) : null;
}
}
}
24 changes: 23 additions & 1 deletion src/nuclei.diagnostics/Profiling/TimerInterval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ internal sealed class TimerInterval : ITimerInterval
/// </summary>
private readonly IIntervalOwner m_Owner;

/// <summary>
/// The group to which the current interval belongs.
/// </summary>
private readonly TimingGroup m_Group;

/// <summary>
/// The description for the current interval.
/// </summary>
Expand All @@ -41,17 +46,23 @@ internal sealed class TimerInterval : ITimerInterval
/// Initializes a new instance of the <see cref="TimerInterval"/> class.
/// </summary>
/// <param name="owner">The objects which owns the current interval.</param>
/// <param name="group">The group to which the current interval belongs.</param>
/// <param name="intervalDescription">The description for the current interval.</param>
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="owner"/> is <see langword="null" />.
/// </exception>
internal TimerInterval(IIntervalOwner owner, string intervalDescription)
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="group"/> is <see langword="null" />.
/// </exception>
internal TimerInterval(IIntervalOwner owner, TimingGroup group, string intervalDescription)
{
{
Lokad.Enforce.Argument(() => owner);
Lokad.Enforce.Argument(() => group);
}

m_Owner = owner;
m_Group = group;
m_IntervalDescription = intervalDescription;
}

Expand Down Expand Up @@ -85,6 +96,17 @@ public void Start()
m_StartTicks = m_Owner.CurrentTicks;
}

/// <summary>
/// Gets the group to which the current interval belongs.
/// </summary>
public TimingGroup Group
{
get
{
return m_Group;
}
}

/// <summary>
/// Gets the description for the current interval.
/// </summary>
Expand Down
70 changes: 70 additions & 0 deletions src/nuclei.diagnostics/Profiling/TimingGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//-----------------------------------------------------------------------
// <copyright company="Nuclei">
// Copyright 2013 Nuclei. Licensed under the Apache License, Version 2.0.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Globalization;

namespace Nuclei.Diagnostics.Profiling
{
/// <summary>
/// Defines an ID number for groups of timing intervals.
/// </summary>
[Serializable]
public sealed class TimingGroup : Id<TimingGroup, Guid>
{
/// <summary>
/// Returns a new Guid.
/// </summary>
/// <returns>A new Guid.</returns>
private static Guid Next()
{
return Guid.NewGuid();
}

/// <summary>
/// Initializes a new instance of the <see cref="TimingGroup"/> class.
/// </summary>
public TimingGroup()
: this(Next())
{
}

/// <summary>
/// Initializes a new instance of the <see cref="TimingGroup"/> class.
/// </summary>
/// <param name="id">The Guid for the ID.</param>
private TimingGroup(Guid id)
: base(id)
{
}

/// <summary>
/// Performs the actual act of creating a copy of the current ID number.
/// </summary>
/// <param name="value">The internally stored value.</param>
/// <returns>
/// A copy of the current ID number.
/// </returns>
protected override TimingGroup Clone(Guid value)
{
return new TimingGroup(value);
}

/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return string.Format(
CultureInfo.InvariantCulture,
"IntervalGroup: [{0}]",
InternalValue);
}
}
}

0 comments on commit f2c9e5d

Please sign in to comment.