|
-
Nov 11th, 2010, 01:49 PM
#1
A Lightweight Profiler
At one point, I wanted to figure out what was slowing down a program that I largely had not written. I wanted to be able to time multiple nested pieces, and multiple sequential pieces, all at the same time. I wanted the timing to be pretty accurate, which meant using the Stopwatch object and also meant that the code had to have little time impact on its own, and I wanted it to be cheap. Naturally, you can buy commercial profilers, and there are some in some versions of VB. I didn't have access to either of those when I wrote this, and, quite frankly, the ease of use is such that I still rather like it.
Here's the code. Explanation will follow:
Code:
Public Class ProfileTimer
Private mStop As Stopwatch
Private mRegister As System.Collections.Generic.List(Of TimeHolder)
Private mIsRunning As Boolean
Private mLock As Boolean
Public Shared TheTime As New ProfileTimer
Private Sub New()
mStop = New Stopwatch
mRegister = New System.Collections.Generic.List(Of TimeHolder)
mIsRunning = False
mLock = False
End Sub
#Region "Properties"
Public ReadOnly Property GetString(ByVal index As Integer) As String
Get
If index >= 0 AndAlso index < mRegister.Count Then
Return mRegister(index).TokenName & ": " & (mRegister(index).StopTime - mRegister(index).StartTime).ToString & "ms"
Else
Return ""
End If
End Get
End Property
Public ReadOnly Property Count() As Integer
Get
Return mRegister.Count
End Get
End Property
Public ReadOnly Property GetString(ByVal tokn As String) As String
Get
Dim n As Integer = GetIndex(tokn)
If n >= 0 Then
Return mRegister(n).TokenName & ": " & (mRegister(n).StopTime - mRegister(n).StartTime).ToString & "ms"
Else
Return ""
End If
End Get
End Property
Public ReadOnly Property GetString() As String
Get
If mRegister.Count = 0 Then
Return "No Timing"
Else
Dim x As Integer
Dim sb As New System.Text.StringBuilder
For x = 0 To mRegister.Count - 1
sb.Append(mRegister(x).TokenName & ": " & (mRegister(x).StopTime - mRegister(x).StartTime).ToString & "ms" & Environment.NewLine)
Next
Return sb.ToString
End If
End Get
End Property
Public Property Lock() As Boolean
Get
Return mLock
End Get
Set(ByVal value As Boolean)
mLock = value
End Set
End Property
#End Region
#Region "Methods"
Public Sub StopAll()
Dim x As Integer
Dim th As TimeHolder
For x = 0 To mRegister.Count - 1
If mRegister(x).StopTime = -1 Then
th = mRegister(x)
th.StopTime = mStop.ElapsedMilliseconds
mRegister(x) = th
End If
Next
End Sub
Private Function StopTrack(ByVal tokn As String) As Long
Dim n As Integer = GetIndex(tokn)
Dim th As TimeHolder
If n >= 0 Then
th = mRegister(n)
th.StopTime = mStop.ElapsedMilliseconds
mRegister(n) = th
Return mRegister(n).StopTime - mRegister(n).StartTime
End If
End Function
Private Sub StartTrack(ByVal tokn As String)
mRegister.Add(New TimeHolder(tokn, GetTick))
End Sub
Public Sub Track(ByVal tokn As String)
Dim n As Integer = GetIndex(tokn)
If n = -1 Then
StartTrack(tokn)
ElseIf mRegister(n).StopTime >= 0 Then
If Not Lock Then
mRegister.RemoveAt(n)
StartTrack(tokn)
End If
Else
StopTrack(tokn)
End If
End Sub
Private Function GetTick() As Long
If mIsRunning Then
Return mStop.ElapsedMilliseconds
Else
mIsRunning = True
mStop.Start()
Return 0
End If
End Function
Private Function GetIndex(ByVal tokn As String) As Integer
Dim x As Integer
For x = 0 To mRegister.Count - 1
If mRegister(x).TokenName = tokn Then
Return x
End If
Next
Return -1
End Function
#End Region
End Class
Public Structure TimeHolder
Implements IEquatable(Of String)
Public StartTime As Long
Public StopTime As Long
Public TokenName As String
Public Sub New(ByVal tokn As String, ByVal sTime As Long)
StartTime = sTime
TokenName = tokn
StopTime = -1
End Sub
Public Function Equals1(ByVal other As String) As Boolean Implements System.IEquatable(Of String).Equals
Return (other = TokenName)
End Function
End Structure
There is little to this that needs much explanation. The whole thing is a Singleton class with one significant method: Track.
To use the class, copy it somewhere in the project. You access the Singleton via: ProfileTimer.TheTime
A timed segment is called a track, and each track has a 'name', which can be any string. To start timing a segment, call Track() while passing in a name. Calling Track a second time with the same name will stop the segment. Therefore, you can write code like this:
Code:
Public Sub Foo()
'Start track "a" to time the whole loop.
ProfileTimer.TheTime.Track("a")
For x as Integer = 0 To 1000
'Start track "b" to time just this call.
ProfileTimer.TheTime.Track("b")
Bar()
'Stop track "b".
ProfileTimer.TheTime.Track("b")
Next
End Sub
That's a pretty silly example, because track "b" will just be starting and stopping with each iteration, while track "a" is really timing almost the same thing, but it does give some idea of what can be done with the timer. A more complete example would be tedious.
At any time, you can call StopAll to stop all running tracks, and you can call one of the GetString properties to see the result. If you pass in no arguments to GetString, it will return the time for each track, with one track per line. Alternatively, you can get the time for a single track by passing in the track name, or by passing in the index into the track list.
There is some other functionality in there about locking and unlocking tracks. Frankly, I wrote this so long ago that I don't remember how that works, but it has to do with stopping and starting tracks, and I have never actually used it. The rest works well. That part....well, who knows, and frankly, who cares. Excess functionality with little utility.
My usual boring signature: Nothing
 
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|