Click here to Skip to main content
15,893,266 members
Articles / Programming Languages / Visual Basic

Comparative Speed Testing

Rate me:
Please Sign up or sign in to vote.
3.57/5 (9 votes)
28 May 2008CPOL6 min read 29.2K   548   15  
A simple-to-use class for performing comparative, non-benchmarked speed tests when optimising code for execution speed.
#Region "********** TO DO **********"

'### TO DO #####################################################################
'---------------------------------------
' DATE: 2008-05-11
' WHO:  Warrick Procter
' DESCRIPTION:
' o Test
'### TO DO #####################################################################

#End Region

#Region "[=== OPTIONS ===]"

Option Strict On
Option Explicit On
Option Compare Binary

#End Region

#Region "[=== IMPORTS ===]"

#End Region

Namespace ZED.Utility.Development

  ''' <copyright>
  '''###########################################################################
  '''##                Copyright (c) 2008 Warrick Procter.                    ##
  '''##                                                                       ##
  '''## This work is covered by the "Code Project Open License", a copy of    ##
  '''## which is enclosed with this package as:                               ##
  '''##         "Code Project Open License (CPOL).txt",                       ##
  '''## and is available from http://www.codeproject.com/.                    ##
  '''##                                                                       ##
  '''## No other use is permitted without the express prior written           ##
  '''## permission of Warrick Procter.                                        ##
  '''## For permission, try these contact addresses (current at the time of   ##
  '''## writing):                                                             ##
  '''##     procter_AT_xtra_DOT_co_DOT_nz                                     ##
  '''##     The address for service of company "ZED Limited", New Zealand.    ##
  '''##                                                                       ##
  '''## Redistribution and use in source and binary forms, with or without    ##
  '''## modification, are permitted provided that the following conditions    ##
  '''## are met:                                                              ##
  '''## 1. Redistributions of source code must retain the above copyright     ##
  '''##    notice, this list of conditions and the following disclaimer.      ##
  '''## 2. Redistributions in binary form must reproduce the above copyright  ##
  '''##    notice in the documentation and/or other materials provided with   ##
  '''##    the distribution.                                                  ##
  '''###########################################################################
  ''' </copyright>
  ''' <disclaimer>
  '''###########################################################################
  '''## REPRESENTATIONS, WARRANTIES AND DISCLAIMER                            ##
  '''## ------------------------------------------                            ##
  '''## THIS WORK IS PROVIDED "AS IS", "WHERE IS" AND "AS AVAILABLE", WITHOUT ##
  '''## ANY EXPRESS OR IMPLIED WARRANTIES OR CONDITIONS OR GUARANTEES. YOU,   ##
  '''## THE USER, ASSUME ALL RISK IN ITS USE, INCLUDING COPYRIGHT             ##
  '''## INFRINGEMENT, PATENT INFRINGEMENT, SUITABILITY, ETC. AUTHOR EXPRESSLY ##
  '''## DISCLAIMS ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS, ##
  '''## INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF             ##
  '''## MERCHANTABILITY, MERCHANTABLE QUALITY OR FITNESS FOR A PARTICULAR     ##
  '''## PURPOSE, OR ANY WARRANTY OF TITLE OR NON-INFRINGEMENT, OR THAT THE    ##
  '''## WORK (OR ANY PORTION THEREOF) IS CORRECT, USEFUL, BUG-FREE OR FREE OF ##
  '''## VIRUSES. YOU MUST PASS THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE ##
  '''## WORK OR DERIVATIVE WORKS.                                             ##
  '''###########################################################################
  ''' </disclaimer>
  ''' <history>
  ''' 2008-05-11 [Warrick Procter] Created.
  ''' </history>
  ''' <summary>
  ''' clsBaseSpeedTestAB - Speed tests.
  ''' </summary>
  ''' <overview>
  ''' </overview>
  ''' <remarks>
  ''' </remarks>
  ''' <notes>
  ''' </notes>
  Public MustInherit Class clsBaseSpeedTestAB

#Region "[=== FIELDS ===]"

    Protected Const kREPETITIONS As Int32 = 100000

    Protected f_Repetitions As Int32

    Protected f_ElapsedA As TimeSpan
    Protected f_DescribeA As String
    Protected f_RepeatA As Int32
    Protected f_TimeSpanA As TimeSpan
    Protected f_UnitsA As Double

    Protected f_ElapsedB As TimeSpan
    Protected f_DescribeB As String
    Protected f_RepeatB As Int32
    Protected f_TimeSpanB As TimeSpan
    Protected f_UnitsB As Double

    Protected f_ElapsedControl As TimeSpan
    Protected f_DescribeControl As String = "Empty method to measure method overhead."
    Protected f_RepeatControl As Int32

    Protected f_UnitInt As Int32
    Protected f_UnitStr As String

#End Region

#Region "[=== CONSTRUCTORS DESTRUCTORS INITIALISATION ===]"

    ''' <summary>
    ''' Construct a default new speed test with kREPETITIONS repetitions.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()
      Me.f_Repetitions = kREPETITIONS
      Me.Initialise()
    End Sub

    ''' <summary>
    ''' Construct a new speed test with prescribed repetitions.
    ''' </summary>
    ''' <param name="aRepetitions"></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal aRepetitions As Int32)
      Me.f_Repetitions = aRepetitions
      Me.Initialise()
    End Sub

    ''' <summary>
    ''' Initialise fields.
    ''' </summary>
    ''' <remarks></remarks>
    Protected Overridable Sub Initialise()
      Me.f_ElapsedA = TimeSpan.Zero
      Me.f_RepeatA = 0
      Me.f_ElapsedB = TimeSpan.Zero
      Me.f_RepeatB = 0
      Me.f_ElapsedControl = TimeSpan.Zero
      Me.f_RepeatControl = 0
    End Sub

#End Region

#Region "[=== PUBLIC PROPERTIES ===]"

    ''' <summary>
    ''' Get the net millisecond time elapsed for process A repetitions.
    ''' </summary>
    ''' <value>Timespan in milliseconds.</value>
    ''' <remarks></remarks>
    Public ReadOnly Property NetTimeA() As TimeSpan
      Get
        Return Me.f_TimeSpanA
      End Get
    End Property

    ''' <summary>
    ''' Get the net millisecond time elapsed for process B repetitions.
    ''' </summary>
    ''' <value>Timespan in milliseconds.</value>
    ''' <remarks></remarks>
    Public ReadOnly Property NetTimeB() As Double
      Get
        Dim xDblB, xDblC As Double
        xDblB = 1000.0 * Me.f_ElapsedB.TotalSeconds / Me.f_RepeatB
        xDblC = 1000.0 * Me.f_ElapsedControl.TotalSeconds / Me.f_RepeatControl
        If xDblC > xDblB Then
          Return 0.0
        Else
          Return xDblB - xDblC
        End If
      End Get
    End Property

    ''' <summary>
    ''' Get the total time elapsed for process A repetitions.
    ''' </summary>
    ''' <value>Timespan.</value>
    ''' <remarks></remarks>
    Public ReadOnly Property TotalTimeA() As TimeSpan
      Get
        Return Me.f_ElapsedA
      End Get
    End Property

    ''' <summary>
    ''' Get the total time elapsed for process B repetitions.
    ''' </summary>
    ''' <value>Timespan.</value>
    ''' <remarks></remarks>
    Public ReadOnly Property TotalTimeB() As TimeSpan
      Get
        Return Me.f_ElapsedB
      End Get
    End Property

    ''' <summary>
    ''' Get the total time elapsed for process Control. repetitions
    ''' </summary>
    ''' <value>Timespan.</value>
    ''' <remarks></remarks>
    Public ReadOnly Property TotalTimeControl() As TimeSpan
      Get
        'dim xSpan as TimeSpan = new TimeSpan(
        Return New TimeSpan(CLng(Me.f_ElapsedControl.Ticks / 3))
      End Get
    End Property

    ''' <summary>
    ''' Get/Set the number of test repetitions.
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public Property Repetitions() As Int32
      Get
        Return Me.f_Repetitions
      End Get
      Set(ByVal value As Int32)
        Me.f_Repetitions = value
        Me.Initialise()
      End Set
    End Property

#End Region

#Region "[=== PROTECTED METHODS MUST OVERRIDE ===]"

    ''' <summary>
    ''' Overriden speed test A setup.
    ''' </summary>
    ''' <remarks></remarks>
    Protected MustOverride Sub SetUpTestA()

    ''' <summary>
    ''' Overriden speed test A setup.
    ''' </summary>
    ''' <remarks></remarks>
    Protected MustOverride Sub SetUpTestB()

    ''' <summary>
    ''' Overridden Speed Test A.
    ''' </summary>
    ''' <remarks></remarks>
    Protected MustOverride Sub SpeedTestA()

    ''' <summary>
    ''' Overridden Speed Test B.
    ''' </summary>
    ''' <remarks></remarks>
    Protected MustOverride Sub SpeedTestB()

#End Region

#Region "[=== PROTECTED METHODS OVERRIDABLE ===]"

    ''' <summary>
    ''' OVERRIDABLE: Get the number of repetitions.
    ''' </summary>
    ''' <returns>Int32 number of repetitions.</returns>
    Protected Overridable Function GetRepetitions() As Int32
      ' Just return our stored repetition.
      Return Me.f_Repetitions
    End Function

    ''' <summary>
    ''' OVERRIDABLE: Get the results stringbuilder
    ''' </summary>
    ''' <returns></returns>
    Protected Overridable Function GetResults() As System.Text.StringBuilder
      Dim xStr As New System.Text.StringBuilder

      xStr.AppendLine("TEST RESULTS:")

      xStr.Append(Me.f_Repetitions.ToString("#,##0"))
      xStr.AppendLine(" repetitions.")

      xStr.Append("Test A: ")
      xStr.AppendLine(Me.f_DescribeA)

      xStr.Append("Test B: ")
      xStr.AppendLine(Me.f_DescribeB)

      xStr.Append(Me.f_ElapsedControl.ToString.PadRight(17, " "c))
      xStr.AppendLine(" hh:mm:ss.ff    Equivalent Elapsed Time Control Process.")

      xStr.Append(Me.f_ElapsedA.ToString.PadRight(17, " "c))
      xStr.AppendLine(" hh:mm:ss.ff    Total Elapsed Time Process A.")

      xStr.Append(Me.f_ElapsedB.ToString.PadRight(17, " "c))
      xStr.AppendLine(" hh:mm:ss.ff    Total Elapsed Time Process B.")

      xStr.Append(Me.f_TimeSpanA.ToString.PadRight(17, " "c))
      xStr.AppendLine(" hh:mm:ss.ff    Net Elapsed Time Process A.")

      xStr.Append(Me.f_TimeSpanB.ToString.PadRight(17, " "c))
      xStr.AppendLine(" hh:mm:ss.ff    Net Elapsed Time Process B.")

      xStr.Append("Net Unit Processing Time A: ")
      xStr.Append(Me.f_UnitsA.ToString("#,##0.000").PadLeft(13, " "c))
      xStr.Append(" ")
      xStr.AppendLine(Me.f_UnitStr)

      xStr.Append("Net Unit Processing Time B: ")
      xStr.Append(Me.f_UnitsB.ToString("#,##0.000").PadLeft(13, " "c))
      xStr.Append(" ")
      xStr.AppendLine(Me.f_UnitStr)

      xStr.AppendLine(Me.GetResultsSummary.ToString)

      Return xStr
    End Function

    ''' <summary>
    ''' OVERRIDABLE: Get the summary of results.
    ''' </summary>
    ''' <returns></returns>
    Protected Overridable Function GetResultsSummary() As System.Text.StringBuilder
      Dim xStr As New System.Text.StringBuilder
      Dim xFraction As Double

      ' Calculate the fraction A divided by B.
      If Me.f_TimeSpanB.Ticks = 0 Then
        xFraction = 0.0
      Else
        xFraction = 100.0 * Me.f_TimeSpanA.Ticks / Me.f_TimeSpanB.Ticks
      End If

      xStr.Append(xFraction.ToString("#,##0.000"))
      xStr.AppendLine("%    Percentage: Process A divided by Process B.")

      Return xStr
    End Function

    ''' <summary>
    ''' OVERRIDABLE: Conduct the speed test.
    ''' </summary>
    ''' <remarks></remarks>
    Public Overridable Sub RunTest()
      ' Define a repetition count.
      Dim xRepetitions As Int32

      ' Get the number of repetitions.
      xRepetitions = Me.GetRepetitions
      Me.f_Repetitions = xRepetitions
      ' Check repetitions.
      If xRepetitions <= 0 Then
        MsgBox("Please set a positive number of repetitions.")
        Return
      End If

      ' Reset the counters.
      Me.Initialise()

      ' Run Control Test and Test A.
      Me.RunTestA()

      ' Run Control Test and Test B.
      Me.RunTestB()

      Dim xCnt As Int32
      Dim xStart As DateTime
      Dim xSpan As TimeSpan

      ' Run the final Control Test.
      xStart = Now
      For xCnt = 1 To xRepetitions
        Me.SpeedTestControl()
      Next
      xSpan = Now - xStart
      Me.f_ElapsedControl += xSpan
      Me.f_RepeatControl += xRepetitions

      ' Calculate the relative elapsed time for Control (runs more than once).
      Dim xTicks As Int64
      ' Get total ticks for control runs.
      xTicks = Me.f_ElapsedControl.Ticks
      ' Scale back according to the number of runs.
      xTicks = CLng(xTicks * Me.f_Repetitions / Me.f_RepeatControl)
      ' Calculate the equivalent net elapsed time for Control.
      Me.f_ElapsedControl = New TimeSpan(xTicks)

      ' Calculate the units.
      Me.f_UnitInt = 1
      Me.f_UnitStr = "secs"
      Me.f_UnitsA = Me.f_TimeSpanA.TotalSeconds / Me.f_RepeatA
      Me.f_UnitsB = Me.f_TimeSpanB.TotalSeconds / Me.f_RepeatA

      ' Scale the units.
      Me.ScaleUnits()

    End Sub

    ''' <summary>
    ''' OVERRIDABLE: Run the Control Test and Test A.
    ''' </summary>
    ''' <remarks></remarks>
    Protected Overridable Sub RunTestA()
      Dim xCnt As Int32
      Dim xStart As DateTime
      Dim xSpanControl As TimeSpan
      Dim xSpanTest As TimeSpan

      ' Run Control Test.
      xStart = Now
      For xCnt = 1 To Me.f_Repetitions
        Me.SpeedTestControl()
      Next
      xSpanControl = Now - xStart
      Me.f_ElapsedControl += xSpanControl
      Me.f_RepeatControl += Me.f_Repetitions

      ' Run Test A.
      Me.SetUpTestA()
      xStart = Now
      For xCnt = 1 To Me.f_Repetitions
        Me.SpeedTestA()
      Next
      xSpanTest = Now - xStart
      Me.f_ElapsedA = xSpanTest
      Me.f_RepeatA = Me.f_Repetitions

      ' Calculate the net elapsed timespan for the Test.
      If xSpanTest.Ticks > xSpanControl.Ticks Then
        Me.f_TimeSpanA = Me.f_ElapsedA - xSpanControl
      Else
        Me.f_TimeSpanA = TimeSpan.Zero
      End If

    End Sub

    ''' <summary>
    ''' OVERRIDABLE: Run the Control Test and Test B.
    ''' </summary>
    ''' <remarks></remarks>
    Protected Overridable Sub RunTestB()
      Dim xCnt As Int32
      Dim xStart As DateTime
      Dim xSpanControl As TimeSpan
      Dim xSpanTest As TimeSpan

      ' Run Control Test.
      xStart = Now
      For xCnt = 1 To Me.f_Repetitions
        Me.SpeedTestControl()
      Next
      xSpanControl = Now - xStart
      Me.f_ElapsedControl += xSpanControl
      Me.f_RepeatControl += Me.f_Repetitions

      ' Run Test B.
      Me.SetUpTestB()
      xStart = Now
      For xCnt = 1 To Me.f_Repetitions
        Me.SpeedTestB()
      Next
      xSpanTest = Now - xStart
      Me.f_ElapsedB = xSpanTest
      Me.f_RepeatB = Me.f_Repetitions

      ' Calculate the net elapsed timespan for the Test.
      If xSpanTest.Ticks > xSpanControl.Ticks Then
        Me.f_TimeSpanB = Me.f_ElapsedB - xSpanControl
      Else
        Me.f_TimeSpanB = TimeSpan.Zero
      End If

    End Sub

    ''' <summary>
    ''' OVERRIDABLE: Scale result units so they register between 0.000 and 1000.999.
    ''' </summary>
    ''' <remarks></remarks>
    Protected Overridable Sub ScaleUnits()

      ' Scale the units down.
      Do While ((Me.f_UnitsA > 100) Or (Me.f_UnitsB > 100)) _
            And (Me.f_UnitStr <> "hours")
        If Me.f_UnitStr = "secs" Then
          Me.f_UnitsA /= 60.0
          Me.f_UnitsB /= 60.0
          Me.f_UnitStr = "mins"
        ElseIf Me.f_UnitStr = "mins" Then
          Me.f_UnitsA /= 60.0
          Me.f_UnitsB /= 60.0
          Me.f_UnitStr = "hours"
        End If
      Loop

      ' Scale the units up.
      Do While ((Me.f_UnitsA < 1.0) Or (Me.f_UnitsB < 1.0)) _
            And (Me.f_UnitStr <> "nanosecs")
        If Me.f_UnitStr = "secs" Then
          Me.f_UnitsA *= 1000.0
          Me.f_UnitsB *= 1000.0
          Me.f_UnitStr = "millisecs"
        ElseIf Me.f_UnitStr = "millisecs" Then
          Me.f_UnitsA *= 1000.0
          Me.f_UnitsB *= 1000.0
          Me.f_UnitStr = "microsecs"
        ElseIf Me.f_UnitStr = "microsecs" Then
          Me.f_UnitsA *= 1000.0
          Me.f_UnitsB *= 1000.0
          Me.f_UnitStr = "nanosecs"
        End If
      Loop

    End Sub

#End Region

#Region "[=== PUBLIC METHODS NOT OVERRIDABLE ===]"

    ''' <summary>
    ''' Append the results to file.
    ''' </summary>
    ''' <param name="aFilePath">Path of the file to write to.</param>
    ''' <remarks></remarks>
    Public Sub AppendResults(ByVal aFilePath As String)
      Dim xStrm As New System.IO.StreamWriter(aFilePath, True)
      xStrm.WriteLine()
      xStrm.WriteLine(Me.GetResults)
      xStrm.Close()
    End Sub

    ''' <summary>
    ''' Get the string results.
    ''' </summary>
    ''' <returns></returns>
    Public Function Results() As String
      Return Me.GetResults.ToString
    End Function

    ''' <summary>
    ''' Show the results in a message box..
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub ShowResults()
      MsgBox(Me.GetResults.ToString)
    End Sub

    ''' <summary>
    ''' Control speed test method. Just measures infrastructure overhead.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub SpeedTestControl()
      ' Do nothing in here, we are just measuring the overhead.
    End Sub

    ''' <summary>
    ''' Write the results to file.
    ''' </summary>
    ''' <param name="aFilePath">Path of the file to write to.</param>
    ''' <remarks></remarks>
    Public Sub WriteResults(ByVal aFilePath As String)
      Dim xStrm As New System.IO.StreamWriter(aFilePath)
      xStrm.WriteLine(Me.GetResults)
      xStrm.Close()
    End Sub

    ''' <summary>
    ''' Write the results to file.
    ''' </summary>
    ''' <param name="aFilePath">Path of the file to write to.</param>
    ''' <param name="aAppend">True to append to the file, otherwise False.</param>
    ''' <remarks></remarks>
    Public Sub WriteResults(ByVal aFilePath As String, ByVal aAppend As Boolean)
      Dim xStrm As New System.IO.StreamWriter(aFilePath, aAppend)
      xStrm.WriteLine(Me.GetResults)
      xStrm.Close()
    End Sub

    ''' <summary>
    ''' Write the results to stream.
    ''' </summary>
    ''' <param name="aFileStream">The stream to write to.</param>
    ''' <remarks></remarks>
    Public Sub WriteResults(ByVal aFileStream As System.IO.Stream)
      Dim xStrm As New System.IO.StreamWriter(aFileStream)
      xStrm.WriteLine(Me.GetResults)
    End Sub

#End Region

  End Class

End Namespace

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
ZED
New Zealand New Zealand
A DEC PDP/11 BasicPlus2 developer from the 80s.

Comments and Discussions