Hugoware

The product of a web developer with a little too much caffeine

Posts Tagged ‘Performance

jLinq Performance Analyzed

with 2 comments

After writing this post I went back and rewrote jLinq from the ground up to improve performance.

So here is something interesting. Another developer named Dan Stocker has been working on a Javascript sorting library named jOrder and used jLinq for comparing benchmarks. The results are interesting to say the least.

Dan has a good write up of the results in the wiki for his project.

I’m disappointed, but in a good way. I thought jLinq was ‘good enough’. It performed the way it should and that was good enough for me. These results tell a much different story.

jLinq probably performs well in typical scenarios, whereas these benchmarks are for medium to large arrays queried thousands of times. I’ve used it in several projects with great results.

Regardless, jLinq clearly performs slower than it should. Reviewing the jLinq code I can think of a few potential pain points.

  1. The entire jLinq object and all of the commands are generated each time a query is started. jLinq there should be a way to cache a standard jLinq object and only rebuild it when needed.
  2. jLinq uses a lot of evals that probably could have been avoided if I understood Javascript better at the time. This undoubtedly is going to cause a significant performance hit.

jLinq was written just about a year and a half ago and I’ve since learned some of the finer parts of Javascript which would most likely improve the performance of the library. In fact, seeing this review has really inspired me to get into back into the project and see what kind of improvements I can make.

I appreciate Dan showing these results to me. Sharing your code with others is a great way to get feedback about what you write… even if it is a kick in the butt.

Advertisements

Written by hugoware

August 4, 2010 at 8:43 am

Code Performance Measuring With Enclosures

with 2 comments

I’m sure that there are many libraries available to help measure the execution of time inside of an application but I spent some time the other day writing a quick class to help me time segments of code.

The idea was to use an Anonymous Method to wrap a section of code with a Stopwatch to check the total time of execution. Additionally, since methods could be Anonymous, you could nest the measurements to see how long specific sections would take.

Anyways, here is some source code to play around with.

/// <summary>
/// Simple measuring of sections of code
/// </summary>
public class CodeBlockTimer {

    #region Private Fields

    //holds the current time for this measurement
    private Stopwatch _OverallTime;

    //container for the measured code sections
    private List<object> _Blocks = new List<object>();

    //the nested level for the code block
    private int _Level = 0;

    #endregion

    #region Methods

    /// <summary>
    /// Stops this code block timer
    /// </summary>
    public void Stop() {
        this._OverallTime.Stop();
    }

    /// <summary>
    /// Adds a block of code to measure
    /// </summary>
    public void Measure(Action block) {
        this.Measure(block.Method.Name, block);
    }

    /// <summary>
    /// Adds a block of code to measure with a defined name
    /// </summary>
    public void Measure(string identity, Action block) {

        //start the overall timer if needed
        if (this._OverallTime == null) {
            this._OverallTime = Stopwatch.StartNew();
        }
        //check and make sure this timer wasn't already stopped
        else if (!this._OverallTime.IsRunning) {
            throw new InvalidOperationException(
                "This CodeBlockTimer has already been stopped and cannot measure additional code blocks."
                );
        }

        //create the container
        _StartBlock start = new _StartBlock(identity, this._Level++);
        _EndBlock end = new _EndBlock(start);

        //run this section of code
        this._Blocks.Add(start);
        start.Timer = Stopwatch.StartNew();
        block();
        start.Timer.Stop();
        this._Blocks.Add(end);

        //and revert the level
        this._Level--;

    }

    /// <summary>
    /// Writes the results to the Console
    /// </summary>
    public void ToConsole() {

        //make sure all of the jobs have finished
        if (this._Level > 0) {
            throw new InvalidOperationException(
                "Cannot report the final summary until all code blocks have completed."
                );
        }

        //stop the overall timer
        if (this._OverallTime.IsRunning) {
            this._OverallTime.Stop();
        }

        //showing code groups
        Func<int, string> treeLine = (count) => {
            string start = string.Empty;
            for (var i = 0; i < count; i++) {
                start = string.Concat(start, "|  ");
            }
            return start;
        };

        //display each code block
        foreach (object item in this._Blocks) {

            //if this is a starting block, show the work has started
            if (item is _StartBlock) {
                _StartBlock block = item as _StartBlock;
                Console.WriteLine(
                    "{0}[Begin] {1}: {2}",
                    treeLine(block.Level),
                    block.Identity,
                    block.Level
                    );
            }
            //if this is the closing block, display the timer result
            else if (item is _EndBlock) {
                _EndBlock block = item as _EndBlock;
                Console.WriteLine(
                    "{0}[End] {1}: {2} ({3})",
                    treeLine(block.StartingBlock.Level),
                    block.StartingBlock.Identity,
                    block.StartingBlock.Level,
                    block.StartingBlock.ToTimeString()
                    );
            }

        }

        //and display the summary
        Console.WriteLine(
            "\nTotal Blocks: {0}\nTotal Time: {1}", 
            this._Blocks.Where(item => item is _StartBlock).Count(),
            CodeBlockTimer._ToTimeString(this._OverallTime.ElapsedMilliseconds)
            );
    }

    #endregion

    #region Static Methods

    //creates a meaningful time string from some milliseconds
    private static string _ToTimeString(long ms) {
        if (ms > 60000) {
            return string.Format("{0:0.0}min", (double)ms / 60000d);
        }
        else if (ms > 1000) {
            return string.Format("{0:0.0}sec", (double)ms / 1000d);
        }
        else {
            return string.Format("{0}ms", ms);
        }
    }

    #endregion

    #region Timing Classes

    //the marker for the start of a code block
    private class _StartBlock {

        //starts a new block to check the starting time
        public _StartBlock(string identity, int level) {
            this.Identity = identity;
            this.Level = level;
        }

        //the identifying name for this code block
        public string Identity { get; private set; }

        //the level this starts at
        public int Level { get; private set; }

        //the timer for this code block
        public Stopwatch Timer { get; set; }
        
        //provides a meaningful time value
        public string ToTimeString() {
            return CodeBlockTimer._ToTimeString(this.Timer.ElapsedMilliseconds);
        }
    }

    //marker for the finishing of a code block
    private class _EndBlock {

        //starts a new block to check the ending time time
        public _EndBlock(_StartBlock start) {
            this.StartingBlock = start;
        }

        //the open block this code was started in
        public _StartBlock StartingBlock { get; private set; }
    }

    #endregion

}

You can use the code inline with existing code to measure sections of the code. If you are using an existing method then the name will automatically appear in the final report. Otherwise you can use the default name or provide your own identity (which can help provide meaningful names for Anonymous Methods).

//create a monitor
CodeBlockTimer monitor = new CodeBlockTimer();

//start measuring a block
monitor.Measure("First Method", () => {
    
    //simulate execution time
    Thread.Sleep(300);

    //measure a block without an identity
    monitor.Measure(() => {
        Thread.Sleep(500);

        //measure a defined method
        monitor.Measure(DoSomething);
    });

    //provide an identity for an anonymoure method
    monitor.Measure("Quick Call", () => {
        Thread.Sleep(250);
    });

    //more simulated execution time
    Thread.Sleep(300);

});

//stop measuring the timer (or simply call ToConsole())
monitor.Stop();

//Display the results of the method to the console
monitor.ToConsole();

And the results…


[Begin] First Method: 0
| [Begin] <Main>b__1: 1
| | [Begin] DoSomething: 2
| | [End] DoSomething: 2 (1000ms)
| [End] <Main>b__1: 1 (1.5sec)
| [Begin] Quick Call: 1
| [End] Quick Call: 1 (250ms)
[End] First Method: 0 (2.4sec)

Total Blocks: 4
Total Time: 2.4sec

Anyways, I’m sure there is a hundred other ways you could get better results — but this might be good enough for small quick measurements.

Written by hugoware

November 18, 2009 at 11:39 pm