Skip to content

Instantly share code, notes, and snippets.

@ewilde
Created January 31, 2013 15:57
Show Gist options
  • Save ewilde/4683898 to your computer and use it in GitHub Desktop.
Save ewilde/4683898 to your computer and use it in GitHub Desktop.
Create a operation timing class
/// <summary>
/// The result of a timing operation
/// </summary>
public interface IOperationResult : IDisposable, ISplitTimer
{
/// <summary>
/// Gets or sets the elapsed time this operation took to complete.
/// </summary>
TimeSpan Elapsed { get; set; }
/// <summary>
/// Gets or sets the start point in milliseconds.
/// </summary>
/// <value>
/// The start offset in milliseconds.
/// </value>
long StartOffset { get; set; }
/// <summary>
/// Gets the split times associated with this instance.
/// </summary>
/// <value>
/// The split times.
/// </value>
IDictionary<string, IOperationResult> SplitTimes { get; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
string Name { get; set; }
}
using System;
using StructureMap;
/// <summary>
/// Records length of time a logical operation takes
/// </summary>
[PluginFamily(IsSingleton = true)]
public interface IOperationTimer
{
/// <summary>
/// Begins the timing a new operation.
/// </summary>
/// <param name="operationName">Name of the operation.</param>
/// <returns>Returns the operation result, note this implements <see cref="IDisposable"/> so you can wrap in a using. Calling dispose implicitly ends the operation.</returns>
IOperationResult Begin(string operationName);
/// <summary>
/// Ends the specified operation being timed.
/// </summary>
/// <param name="operationName">Name of the operation.</param>
/// <returns>Returns the operation result</returns>
IOperationResult End(string operationName);
/// <summary>
/// Ends the specified operation being timed.
/// </summary>
/// <param name="operationResult">Operation result.</param>
/// <returns>Returns the operation result</returns>
IOperationResult End(IOperationResult operationResult);
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
/// <summary>
/// An operation result, returned from <see cref="IOperationTimer.Begin"/>
/// </summary>
public class OperationResult : IOperationResult
{
/// <summary>
/// Tracks if this instance has been disposed or not.
/// </summary>
private bool disposed;
/// <summary>
/// Initializes a new instance of the <see cref="OperationResult" /> class.
/// </summary>
/// <param name="timer">The timer.</param>
public OperationResult(OperationTimer timer)
{
this.Timer = timer;
this.SplitTimes = new Dictionary<string, IOperationResult>();
}
/// <summary>
/// Finalizes an instance of the <see cref="OperationResult" /> class.
/// </summary>
~OperationResult()
{
this.Dispose(false);
}
/// <summary>
/// Gets or sets the timer.
/// </summary>
/// <value>
/// The timer.
/// </value>
public OperationTimer Timer { get; set; }
/// <summary>
/// Gets or sets the elapsed time this operation took to complete.
/// </summary>
public TimeSpan Elapsed { get; set; }
/// <summary>
/// Gets or sets the start point in milliseconds.
/// </summary>
/// <value>
/// The start offset in milliseconds.
/// </value>
public long StartOffset { get; set; }
/// <summary>
/// Gets or sets the split times.
/// </summary>
/// <value>
/// The split times.
/// </value>
public IDictionary<string, IOperationResult> SplitTimes { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; set; }
/// <inheritdoc />
public IOperationResult Begin(string operationName)
{
var split = this.Timer.Begin(operationName);
this.SplitTimes.Add(operationName, split);
return split;
}
/// <inheritdoc />
public IOperationResult End(string operationName)
{
return this.End(this.SplitTimes[operationName]);
}
/// <inheritdoc />
public IOperationResult End(IOperationResult operationResult)
{
operationResult.Dispose();
return operationResult;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <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()
{
var result = new StringBuilder();
result.AppendFormat(CultureInfo.CurrentCulture, "{0, -12}{1, 12:N0}{2}", "Name", "Time", Environment.NewLine);
result.AppendFormat(CultureInfo.CurrentCulture, "{0, -12:N0}{1, 10:N0}ms", this.Name, this.Elapsed.TotalMilliseconds);
return result.ToString();
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
if (this.Timer != null)
{
this.Timer.End(this);
}
}
this.disposed = true;
}
}
}
using System;
using System.Diagnostics;
using System.Timers;
/// <summary>
/// Records length of time a logical operation takes
/// </summary>
public class OperationTimer : IOperationTimer
{
/// <summary>
/// The stopwatch used to record the length of operations.
/// </summary>
private Stopwatch stopwatch;
/// <summary>
/// Initializes a new instance of the <see cref="OperationTimer" /> class.
/// </summary>
public OperationTimer()
{
this.stopwatch = new Stopwatch();
}
/// <summary>
/// Begins the timing a new operation.
/// </summary>
/// <param name="operationName">Name of the operation.</param>
/// <returns>
/// Returns the operation result, note this implements <see cref="IDisposable" /> so you can wrap in a using. Calling dispose implicitly ends the operation.
/// </returns>
public IOperationResult Begin(string operationName)
{
if (!this.stopwatch.IsRunning)
{
this.stopwatch.Start();
}
var result = new OperationResult(this)
{
Name = operationName,
StartOffset = this.stopwatch.ElapsedMilliseconds
};
return result;
}
/// <summary>
/// Ends the specified operation being timed.
/// </summary>
/// <param name="operationName">Name of the operation.</param>
/// <returns>
/// Returns the operation result
/// </returns>
public IOperationResult End(string operationName)
{
return null;
}
/// <summary>
/// Ends the specified operation being timed.
/// </summary>
/// <param name="operationResult">Operation result.</param>
/// <returns>
/// Returns the operation result
/// </returns>
public IOperationResult End(IOperationResult operationResult)
{
operationResult.Elapsed = TimeSpan.FromMilliseconds(this.stopwatch.ElapsedMilliseconds - operationResult.StartOffset);
return operationResult;
}
}
using System;
using Machine.Fakes;
using Machine.Specifications;
using RADS.Common.Logging;
using RADS.Common.TestFramework;
#pragma warning disable 169
public class When_recording_split_times : WithConcreteSubject<OperationTimer, IOperationTimer>
{
Because of = () =>
{
using (Result = Subject.Begin("LOAD"))
{
System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(100));
using (var split = Result.Begin(SplitTime1))
{
System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(10));
}
}
};
It should_record_the_split_times = () => Result.SplitTimes[SplitTime1].Elapsed.TotalMilliseconds.ShouldBeGreaterThan(10);
It should_record_an_overall_time_greater_than_the_split_time = () =>
Result.Elapsed.ShouldBeGreaterThan(Result.SplitTimes[SplitTime1].Elapsed);
It should_overide_to_string_to_display_split_times = () => Result.ToString().ShouldContain(SplitTime1);
static IOperationResult Result;
const string SplitTime1 = "Split 1";
}
[Subject(typeof(IOperationTimer))]
public class When_starting_a_new_operation : WithConcreteSubject<OperationTimer, IOperationTimer>
{
Because of = () => Result = Subject.Begin("SAVE");
It should_return_an_operation_result = () => Result.ShouldNotBeNull();
static IOperationResult Result;
}
[Subject(typeof(IOperationTimer))]
public class When_disposing_a_new_operation : WithConcreteSubject<OperationTimer, IOperationTimer>
{
Because of = () =>
{
using (Result = Subject.Begin("DOWNLOAD"))
{
System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(10));
}
};
It should_record_the_elapsed_duration = () => Result.Elapsed.TotalMilliseconds.ShouldBeGreaterThan(10);
static IOperationResult Result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment