Performance Timing mit ‚using’ und delegates mit „Conditional“ Methoden
Letztens musste ich die (zeitliche) Performance einer sehr CPU/IO lästigen Operation verbessern.
Da die Operation in diverse kleinere Vorgänge unterteilt war musste ich die Vorgänge ermitteln, welche am meisten Zeit in Anspruch nahmen und das meiste Optimierungspotential hatten. Dafür hab ich eine Klasse entworfen die mit dem „using“-Mechanismus die Dauer einer Operation ermittelt. Dabei bin ich auch darauf gestoßen, dass der C# Compiler Methoden die das „Conditional“ Attribut haben nicht delegates zuweisen kann.
Also, nun zu der Klasse:
public class PerformanceTiming : IDisposable
{
public delegate void LogPerformance(string value);
private DateTime _startTime;
private string _name;
private LogPerformance _logPerformanceMethod;
private const string _startMessage = "PerformanceTiming={0}: Start={1}";
private const string _endMessage = "PerformanceTiming={0}: End={1} Duration={2}msec";
public PerformanceTiming(string name)
{
// cannot directly use Debug.WriteLine(string) as it has the "Conditional" attribute.
// _logPerformanceMethod = System.Diagnostics.Debug.WriteLine(value);
// have to use wrapper function
_logPerformanceMethod = DebugWriteLineWrapper;
_name = name;
_startTime = DateTime.Now;
_logPerformanceMethod(string.Format(_startMessage, _name, _startTime.ToLongTimeString()));
}
public PerformanceTiming(string name, LogPerformance logPerformanceMethod)
{
_logPerformanceMethod = logPerformanceMethod;
_name = name;
_startTime = DateTime.Now;
_logPerformanceMethod(string.Format(_startMessage, _name, _startTime.ToLongTimeString()));
}
public void DebugWriteLineWrapper(string value)
{
System.Diagnostics.Debug.WriteLine(value);
}
#region IDisposable Members
public void Dispose()
{
_logPerformanceMethod(string.Format(_endMessage, _name, DateTime.Now.ToLongTimeString(), (DateTime.Now - _startTime).TotalMilliseconds));
}
#endregion
}
Mehr oder weniger simple, ja, aber es ist eigentlich auch die Idee bzw. die Anwendung der Klasse was die Sache so sinnvoll macht.
using (PerformanceTiming performanceTiming = new PerformanceTiming("HeavyWork"))
{
System.Threading.Thread.Sleep(2000);
}
Damit wird der folgende Output in den Debugoutput stream geschrieben:
PerformanceTiming=HeavyWork: Start=13:12:16
PerformanceTiming=HeavyWork: End=13:12:18 Duration=1999,6672msec
Es wird also die Start-/Endzeit der Operation ausgegeben, plus die Dauer in Millisekunden.
Die Ausgabe kann natürlich ohne Probleme angepasst werden.
Man kann auch eine Methode an das PerformanceTiming Objekt übergeben, welcher dann die Ausgabestrings übergeben werden, so z.B. Console.Writeline.
using (PerformanceTiming performanceTiming = new PerformanceTiming("HeavyWork", Console.WriteLine))
{
System.Threading.Thread.Sleep(2000);
}
Man könnte natürlich auch jede andere Funktion die nur einen (string) Parameter hat übergeben, z.B. StreamWriter.WriteLine um Direkt in eine Datei zu loggen.
Conditional Methoden und delegates, wie macht man das? Man schreibt einfach eine Wrapper funktion die nicht das [Conditional("DEBUG")] Attribut besitzt.
So wird aus:
_logPerformanceMethod = System.Diagnostics.Debug.WriteLine;
Das:
_logPerformanceMethod = DebugWriteLineWrapper;
public void DebugWriteLineWrapper(string value)
{
System.Diagnostics.Debug.WriteLine(value);
}
So, das war nun mein erster mehr oder weniger Sinnvoller Blogeintrag :-)
EDIT: Wie postet man eigentlich code-snippets hier?
EDIT2: I now know the answer thanks to my friend Brendan from Orcs, Goblins and .NET, I will blog about it in a few days.