#region license
// This file is part of Vocaluxe.
//
// Vocaluxe is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vocaluxe is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vocaluxe. If not, see .
#endregion
using System;
using System.Diagnostics;
using VocaluxeLib.Utils;
namespace VocaluxeLib.Log
{
///
/// Class for Benchmarks.
/// Can be used as instance or through the static wrappers.
///
public class CBenchmark : IDisposable
{
// Pool of stop watches shared between all instances (we can get more than 5 but it only keeps 5)
private static readonly CObjectPool _WatchesPool = new CObjectPool(() => new Stopwatch(), 5);
private static readonly double _NanosecPerTick = 1000.0 * 1000.0 * 1000.0 / Stopwatch.Frequency;
// The stop watch used by this instance
private Stopwatch _Watch = null;
// The operation name the will be written to the log on events for this instance
private readonly string _OperationName;
///
/// Creates a new benchmark instance.
///
/// The name of the operation (will be used for log messages).
public CBenchmark(string operationName)
{
_OperationName = operationName;
}
///
/// Starts this benchmark.
///
public void Start()
{
if (_Watch != null)
{
throw new InvalidOperationException("Timer is already running.");
}
_Watch = _WatchesPool.GetObject();
_Watch.Restart();
CLog.Information("Started {StartedOperation}", CLog.Params(_OperationName));
}
///
/// Stops this benchmark.
///
/// If true a success message will be added to the log otherwise a message for failure.
public void Stop(bool success = true)
{
if (_Watch == null)
{
return;
}
_Watch.Stop();
var duration = _GetElapsedTime(_Watch);
_WatchesPool.PutObject(_Watch);
_Watch = null;
if (success)
{
CLog.Information("Finished {StartedOperation} successfully in {Duration:#,##0.00}ms", CLog.Params(_OperationName, duration));
}
else
{
CLog.Information("Failed {StartedOperation} in {Duration:#,##0.00}ms", CLog.Params(_OperationName, duration));
}
}
///
/// Helper method to extract the elapsed time from a stopwatch.
///
/// The stopwatch.
/// The elapsed time of the given stop watch.
private double _GetElapsedTime(Stopwatch watch)
{
if (Stopwatch.IsHighResolution && _NanosecPerTick > 0)
{
return (float)(_NanosecPerTick * watch.ElapsedTicks / (1000.0 * 1000.0));
}
else
{
return watch.ElapsedMilliseconds;
}
}
///
/// Ends a possibly running benchmark (sucessfully).
///
public void Dispose()
{
if (_Watch != null)
{
Stop();
}
}
#region Static functions
///
/// Logs the time from now to disposal of the returned object.
///
/// The name of the operation (will be used for log messages).
/// Object which needs to be disposed to stop the benchmark.
///
///
/// using(CBenchmark.Time("TestOperation")){
/// //do someting
/// }
///
///
public static IDisposable Time(string operationName)
{
var benchmark = new CBenchmark(operationName);
benchmark.Start();
return benchmark;
}
///
/// Logs the time from now to the call of End() on the returned object (== success) or it's disposal (== failure).
///
/// The name of the operation (will be used for log messages).
/// Object to stop the benchmark.
///
///
/// using(var op = CBenchmark.Begin("TestOperation")){
/// //do someting that could fail
/// op.End();
/// }
///
///
public static COperation Begin(string operationName)
{
var benchmark = new CBenchmark(operationName);
benchmark.Start();
return new COperation(benchmark);
}
#region Helper class for operation
///
/// Helper object to end a benchmark (End() for success and Dispose() for failure).
///
public class COperation : IDisposable
{
private readonly CBenchmark _Benchmark;
///
/// Creates a new instance of the benchmark helper object.
///
/// The associated benchmark object.
internal COperation(CBenchmark benchmark)
{
_Benchmark = benchmark;
}
///
/// End the associated benchmark sucessfully.
///
public void End()
{
_Benchmark.Stop(true);
}
///
/// End the associated benchmark not sucessfully.
///
public void Dispose()
{
_Benchmark.Stop(false);
}
}
#endregion
#endregion
}
}