Results 1 to 6 of 6

Thread: A Lightweight Profiler

Hybrid View

  1. #1

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,988

    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

  2. #2
    New Member
    Join Date
    Jan 2012
    Posts
    6

    Re: A Lightweight Profiler

    Please explain how to use this.
    I am having tough time with using this code.
    How do i pass my code file into this? How do I use this code to check a code file?
    Please explain with an example.
    I am in urgent need of this.
    Thanks in advance.

  3. #3
    New Member
    Join Date
    Jan 2012
    Posts
    6

    Re: A Lightweight Profiler

    It also shows an error that bar is not defined.
    Please help.

  4. #4

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,988

    Re: A Lightweight Profiler

    You don't pass a code file into this, it doesn't work that way. The profiler is a class just like any other class in a project. Therefore, you could copy that first code section into any valid code page, either a new one or an existing one (the second code section is just an example, and won't run or even compile, as you found out). The class is a Singleton, which means that there can only be one instance of the class. Just by having that code in the project you can use it, there isn't anything else than needs to be done with that code.

    The second piece of code is an example of how the profiler class would be used. If you have a section of code that you want to time, you add a line like this before the section of code:

    ProfileTimer.TheTime.Track("b")

    because the class is a Singleton, you don't have to create an instance of the ProfileTimer class, there already is one. By calling Track, you start a track with whatever name you pass it as an argumetn (b, in the example). When you call Track a second time with the same name, then the timer stops that track. You don't have to do anything with it at that time, but it is stopped. Therefore, using the class is like using a stopwatch. The first time you call Track, you start the stopwatch. The second time you call Track you stop the stopwatch. You don't have to look at the stopwatch right then, but can wait until later to look at it. That's all the ProfileTimer is doing. Call Track the first time to start, call Track a second time to stop.

    If that was all the class did, there wouldn't be any point in it, because that's the exact functionality of the Stopwatch class. What I added was the names. When you pass in a string, you start timing a track with that name. It is basically the same as having a whole bank of stopwatches, each of which you can stop or start independently of all the others (though you can stop them all with StopAll). The name that you pass to the Track method is how the class tells which one to stop and which one to start. As you can see by looking at the code, there is actually only one stopwatch, for efficiency, but the class acts like there are as many stopwatches as you want, each one named based on the argument you supply to Track.
    My usual boring signature: Nothing

  5. #5
    New Member
    Join Date
    Mar 2012
    Posts
    2

    Re: A Lightweight Profiler

    Thanks really helpful but you explain too much..
    in short ,this is how to use it:

    ProfileTimer.TheTime.Track("a")

    ' Your code you want to examin

    ProfileTimer.TheTime.Track("a")
    MsgBox(ProfileTimer.TheTime.GetString("a"))

  6. #6

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,988

    Re: A Lightweight Profiler

    Yeah, but look at my user title. I ALWAYS write too much. Whether you find the explanation useful is up to the reader.
    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
  •  



Click Here to Expand Forum to Full Width