Click here to Skip to main content
15,896,063 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.
namespace ZED.Utility.Development
{
    using Microsoft.VisualBasic;
    using System;
    using System.IO;
    using System.Text;

    /// <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 abstract class clsBaseSpeedTestAB
    {
        protected string f_DescribeA;
        protected string f_DescribeB;
        protected string f_DescribeControl;
        protected TimeSpan f_ElapsedA;
        protected TimeSpan f_ElapsedB;
        protected TimeSpan f_ElapsedControl;
        protected int f_RepeatA;
        protected int f_RepeatB;
        protected int f_RepeatControl;
        protected int f_Repetitions;
        protected TimeSpan f_TimeSpanA;
        protected TimeSpan f_TimeSpanB;
        protected int f_UnitInt;
        protected double f_UnitsA;
        protected double f_UnitsB;
        protected string f_UnitStr;
        protected const int kREPETITIONS = 0x186a0;

        /// <summary>
        /// Construct a default new speed test with kREPETITIONS repetitions.
        /// </summary>
        /// <remarks></remarks>
        public clsBaseSpeedTestAB()
        {
            this.f_DescribeControl = "Empty method to measure method overhead.";
            this.f_Repetitions = 0x186a0;
            this.Initialise();
        }

        /// <summary>
        /// Construct a new speed test with prescribed repetitions.
        /// </summary>
        /// <param name="aRepetitions"></param>
        /// <remarks></remarks>
        public clsBaseSpeedTestAB(int aRepetitions)
        {
            this.f_DescribeControl = "Empty method to measure method overhead.";
            this.f_Repetitions = aRepetitions;
            this.Initialise();
        }

        /// <summary>
        /// Append the results to file.
        /// </summary>
        /// <param name="aFilePath">Path of the file to write to.</param>
        /// <remarks></remarks>
        public void AppendResults(string aFilePath)
        {
            StreamWriter xStrm = new StreamWriter(aFilePath, true);
            xStrm.WriteLine();
            xStrm.WriteLine(this.GetResults());
            xStrm.Close();
        }

        /// <summary>
        /// OVERRIDABLE: Get the number of repetitions.
        /// </summary>
        /// <returns>Int32 number of repetitions.</returns>
        protected virtual int GetRepetitions()
        {
            return this.f_Repetitions;
        }

        /// <summary>
        /// OVERRIDABLE: Get the results stringbuilder
        /// </summary>
        /// <returns></returns>
        protected virtual StringBuilder GetResults()
        {
            StringBuilder xStr = new StringBuilder();
            xStr.AppendLine("TEST RESULTS:");
            xStr.Append(this.f_Repetitions.ToString("#,##0"));
            xStr.AppendLine(" repetitions.");
            xStr.Append("Test A: ");
            xStr.AppendLine(this.f_DescribeA);
            xStr.Append("Test B: ");
            xStr.AppendLine(this.f_DescribeB);
            xStr.Append(this.f_ElapsedControl.ToString().PadRight(0x11, ' '));
            xStr.AppendLine(" hh:mm:ss.ff    Equivalent Elapsed Time Control Process.");
            xStr.Append(this.f_ElapsedA.ToString().PadRight(0x11, ' '));
            xStr.AppendLine(" hh:mm:ss.ff    Total Elapsed Time Process A.");
            xStr.Append(this.f_ElapsedB.ToString().PadRight(0x11, ' '));
            xStr.AppendLine(" hh:mm:ss.ff    Total Elapsed Time Process B.");
            xStr.Append(this.f_TimeSpanA.ToString().PadRight(0x11, ' '));
            xStr.AppendLine(" hh:mm:ss.ff    Net Elapsed Time Process A.");
            xStr.Append(this.f_TimeSpanB.ToString().PadRight(0x11, ' '));
            xStr.AppendLine(" hh:mm:ss.ff    Net Elapsed Time Process B.");
            xStr.Append("Net Unit Processing Time A: ");
            xStr.Append(this.f_UnitsA.ToString("#,##0.000").PadLeft(13, ' '));
            xStr.Append(" ");
            xStr.AppendLine(this.f_UnitStr);
            xStr.Append("Net Unit Processing Time B: ");
            xStr.Append(this.f_UnitsB.ToString("#,##0.000").PadLeft(13, ' '));
            xStr.Append(" ");
            xStr.AppendLine(this.f_UnitStr);
            xStr.AppendLine(this.GetResultsSummary().ToString());
            return xStr;
        }

        /// <summary>
        /// OVERRIDABLE: Get the summary of results.
        /// </summary>
        /// <returns></returns>
        protected virtual StringBuilder GetResultsSummary()
        {
            double xFraction;
            StringBuilder xStr = new StringBuilder();
            if (this.f_TimeSpanB.Ticks == 0L)
            {
                xFraction = 0.0;
            }
            else
            {
                xFraction = (100.0 * this.f_TimeSpanA.Ticks) / ((double) this.f_TimeSpanB.Ticks);
            }
            xStr.Append(xFraction.ToString("#,##0.000"));
            xStr.AppendLine("%    Percentage: Process A divided by Process B.");
            return xStr;
        }

        /// <summary>
        /// Initialise fields.
        /// </summary>
        /// <remarks></remarks>
        protected virtual void Initialise()
        {
            this.f_ElapsedA = TimeSpan.Zero;
            this.f_RepeatA = 0;
            this.f_ElapsedB = TimeSpan.Zero;
            this.f_RepeatB = 0;
            this.f_ElapsedControl = TimeSpan.Zero;
            this.f_RepeatControl = 0;
        }

        /// <summary>
        /// Get the string results.
        /// </summary>
        /// <returns></returns>
        public string Results()
        {
            return this.GetResults().ToString();
        }

        /// <summary>
        /// OVERRIDABLE: Conduct the speed test.
        /// </summary>
        /// <remarks></remarks>
        public virtual void RunTest()
        {
            int xRepetitions = this.GetRepetitions();
            this.f_Repetitions = xRepetitions;
            if (xRepetitions <= 0)
            {
                Interaction.MsgBox("Please set a positive number of repetitions.", MsgBoxStyle.OkOnly, null);
            }
            else
            {
                this.Initialise();
                this.RunTestA();
                this.RunTestB();
                DateTime xStart = DateAndTime.Now;
                for (int xCnt = 1; xCnt <= xRepetitions; xCnt++)
                {
                    this.SpeedTestControl();
                }
                TimeSpan xSpan = (TimeSpan) (DateAndTime.Now - xStart);
                this.f_ElapsedControl += xSpan;
                this.f_RepeatControl += xRepetitions;
                long xTicks = (long) Math.Round((double) (((double) (this.f_ElapsedControl.Ticks * this.f_Repetitions)) / ((double) this.f_RepeatControl)));
                this.f_ElapsedControl = new TimeSpan(xTicks);
                this.f_UnitInt = 1;
                this.f_UnitStr = "secs";
                this.f_UnitsA = this.f_TimeSpanA.TotalSeconds / ((double) this.f_RepeatA);
                this.f_UnitsB = this.f_TimeSpanB.TotalSeconds / ((double) this.f_RepeatA);
                this.ScaleUnits();
            }
        }

        /// <summary>
        /// OVERRIDABLE: Run the Control Test and Test A.
        /// </summary>
        /// <remarks></remarks>
        protected virtual void RunTestA()
        {
            int xCnt;
            DateTime xStart = DateAndTime.Now;
            for (xCnt = 1; xCnt <= this.f_Repetitions; xCnt++)
            {
                this.SpeedTestControl();
            }
            TimeSpan xSpanControl = (TimeSpan) (DateAndTime.Now - xStart);
            this.f_ElapsedControl += xSpanControl;
            this.f_RepeatControl += this.f_Repetitions;
            this.SetUpTestA();
            xStart = DateAndTime.Now;
            for (xCnt = 1; xCnt <= this.f_Repetitions; xCnt++)
            {
                this.SpeedTestA();
            }
            TimeSpan xSpanTest = (TimeSpan) (DateAndTime.Now - xStart);
            this.f_ElapsedA = xSpanTest;
            this.f_RepeatA = this.f_Repetitions;
            if (xSpanTest.Ticks > xSpanControl.Ticks)
            {
                this.f_TimeSpanA = this.f_ElapsedA - xSpanControl;
            }
            else
            {
                this.f_TimeSpanA = TimeSpan.Zero;
            }
        }

        /// <summary>
        /// OVERRIDABLE: Run the Control Test and Test B.
        /// </summary>
        /// <remarks></remarks>
        protected virtual void RunTestB()
        {
            int xCnt;
            DateTime xStart = DateAndTime.Now;
            for (xCnt = 1; xCnt <= this.f_Repetitions; xCnt++)
            {
                this.SpeedTestControl();
            }
            TimeSpan xSpanControl = (TimeSpan) (DateAndTime.Now - xStart);
            this.f_ElapsedControl += xSpanControl;
            this.f_RepeatControl += this.f_Repetitions;
            this.SetUpTestB();
            xStart = DateAndTime.Now;
            for (xCnt = 1; xCnt <= this.f_Repetitions; xCnt++)
            {
                this.SpeedTestB();
            }
            TimeSpan xSpanTest = (TimeSpan) (DateAndTime.Now - xStart);
            this.f_ElapsedB = xSpanTest;
            this.f_RepeatB = this.f_Repetitions;
            if (xSpanTest.Ticks > xSpanControl.Ticks)
            {
                this.f_TimeSpanB = this.f_ElapsedB - xSpanControl;
            }
            else
            {
                this.f_TimeSpanB = TimeSpan.Zero;
            }
        }

        /// <summary>
        /// OVERRIDABLE: Scale result units so they register between 0.000 and 1000.999.
        /// </summary>
        /// <remarks></remarks>
        protected virtual void ScaleUnits()
        {
            while (((this.f_UnitsA > 100.0) | (this.f_UnitsB > 100.0)) & (this.f_UnitStr != "hours"))
            {
                if (this.f_UnitStr == "secs")
                {
                    this.f_UnitsA /= 60.0;
                    this.f_UnitsB /= 60.0;
                    this.f_UnitStr = "mins";
                }
                else if (this.f_UnitStr == "mins")
                {
                    this.f_UnitsA /= 60.0;
                    this.f_UnitsB /= 60.0;
                    this.f_UnitStr = "hours";
                }
            }
            while (((this.f_UnitsA < 1.0) | (this.f_UnitsB < 1.0)) & (this.f_UnitStr != "nanosecs"))
            {
                if (this.f_UnitStr == "secs")
                {
                    this.f_UnitsA *= 1000.0;
                    this.f_UnitsB *= 1000.0;
                    this.f_UnitStr = "millisecs";
                }
                else if (this.f_UnitStr == "millisecs")
                {
                    this.f_UnitsA *= 1000.0;
                    this.f_UnitsB *= 1000.0;
                    this.f_UnitStr = "microsecs";
                }
                else if (this.f_UnitStr == "microsecs")
                {
                    this.f_UnitsA *= 1000.0;
                    this.f_UnitsB *= 1000.0;
                    this.f_UnitStr = "nanosecs";
                }
            }
        }

        /// <summary>
        /// Overriden speed test A setup.
        /// </summary>
        /// <remarks></remarks>
        protected abstract void SetUpTestA();
        /// <summary>
        /// Overriden speed test A setup.
        /// </summary>
        /// <remarks></remarks>
        protected abstract void SetUpTestB();
        /// <summary>
        /// Show the results in a message box..
        /// </summary>
        /// <remarks></remarks>
        public void ShowResults()
        {
            Interaction.MsgBox(this.GetResults().ToString(), MsgBoxStyle.OkOnly, null);
        }

        /// <summary>
        /// Overridden Speed Test A.
        /// </summary>
        /// <remarks></remarks>
        protected abstract void SpeedTestA();
        /// <summary>
        /// Overridden Speed Test B.
        /// </summary>
        /// <remarks></remarks>
        protected abstract void SpeedTestB();
        /// <summary>
        /// Control speed test method. Just measures infrastructure overhead.
        /// </summary>
        /// <remarks></remarks>
        public void SpeedTestControl()
        {
        }

        /// <summary>
        /// Write the results to stream.
        /// </summary>
        /// <param name="aFileStream">The stream to write to.</param>
        /// <remarks></remarks>
        public void WriteResults(Stream aFileStream)
        {
            new StreamWriter(aFileStream).WriteLine(this.GetResults());
        }

        /// <summary>
        /// Write the results to file.
        /// </summary>
        /// <param name="aFilePath">Path of the file to write to.</param>
        /// <remarks></remarks>
        public void WriteResults(string aFilePath)
        {
            StreamWriter xStrm = new StreamWriter(aFilePath);
            xStrm.WriteLine(this.GetResults());
            xStrm.Close();
        }

        /// <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 void WriteResults(string aFilePath, bool aAppend)
        {
            StreamWriter xStrm = new StreamWriter(aFilePath, aAppend);
            xStrm.WriteLine(this.GetResults());
            xStrm.Close();
        }

        /// <summary>
        /// Get the net millisecond time elapsed for process A repetitions.
        /// </summary>
        /// <value>Timespan in milliseconds.</value>
        /// <remarks></remarks>
        public TimeSpan NetTimeA
        {
            get
            {
                return this.f_TimeSpanA;
            }
        }

        /// <summary>
        /// Get the net millisecond time elapsed for process B repetitions.
        /// </summary>
        /// <value>Timespan in milliseconds.</value>
        /// <remarks></remarks>
        public double NetTimeB
        {
            get
            {
                double xDblB = (1000.0 * this.f_ElapsedB.TotalSeconds) / ((double) this.f_RepeatB);
                double xDblC = (1000.0 * this.f_ElapsedControl.TotalSeconds) / ((double) this.f_RepeatControl);
                if (xDblC > xDblB)
                {
                    return 0.0;
                }
                return (xDblB - xDblC);
            }
        }

        /// <summary>
        /// Get/Set the number of test repetitions.
        /// </summary>
        /// <value></value>
        /// <remarks></remarks>
        public int Repetitions
        {
            get
            {
                return this.f_Repetitions;
            }
            set
            {
                this.f_Repetitions = value;
                this.Initialise();
            }
        }

        /// <summary>
        /// Get the total time elapsed for process A repetitions.
        /// </summary>
        /// <value>Timespan.</value>
        /// <remarks></remarks>
        public TimeSpan TotalTimeA
        {
            get
            {
                return this.f_ElapsedA;
            }
        }

        /// <summary>
        /// Get the total time elapsed for process B repetitions.
        /// </summary>
        /// <value>Timespan.</value>
        /// <remarks></remarks>
        public TimeSpan TotalTimeB
        {
            get
            {
                return this.f_ElapsedB;
            }
        }

        /// <summary>
        /// Get the total time elapsed for process Control. repetitions
        /// </summary>
        /// <value>Timespan.</value>
        /// <remarks></remarks>
        public TimeSpan TotalTimeControl
        {
            get
            {
                return new TimeSpan((long) Math.Round((double) (((double) this.f_ElapsedControl.Ticks) / 3.0)));
            }
        }
    }
}

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