Showing posts with label performance. Show all posts
Showing posts with label performance. Show all posts

Prevent JavaScript code blocking the UI thread with setTimeout + a handy stopWatch to profile JS code

In early January of this year I was implementing some JavaScript code that does a lot of processing on the client side with the help of jQuery. It’s part of a project where my customer asked for the possibility of answering a questionnaire in two ways: using a Wizard (one question at a time) and Listing (all questions at once) in a single form. The client side code for the Listing is the culprit and the subject of this post. It’s also related to this question I posted at StackOverflow on February 19:

Why ASP.NET MVC default Model Binder is slow? It's taking a long time to do its work

That question on SO is the inverse part, that is, the server receiving the client side data, but that’s just the other “part” of the problem which I also managed to solve. I really hope that this post helps you understand the problem with the client side part at least…

One of the things that I didn’t like in that JavaScript code was that while the questions were being processed to be rendered subsequently by the JavaScript/jQuery client side code, the browser UI thread hung/froze a lot of times – I think it’s worth mentioning here that I didn’t get Firefox’s warning prompt for "Unresponsive script". Anyway, the simple fact that the page freezes gives the user and me the developer a feeling that something is wrong with the code. As users we expect things to be fluid – we expect a good UI experience and seeing your browser freezing while loading a web page is not the best impression one may have of your software product.

I’m more experienced with server side code and haven’t ever seen any of my JavaScript code run slow on the client. I then started searching on Google why this was happening and just stumbled on this really helpful and insightful article at O’Reilly Answers:

Yielding with JavaScript Timers

Read it carefully to get a grasp of the concepts involved. I read it maybe thrice at least to understand the code and adapt it to the problem at hand. It fitted so well in my situation that the result I got after adapting the code was mind blowing.

The part that really got my attention was the Timed Code section - way bellow that O’Reilly article. It tells us that batch processing items (questions in my case) instead processing everything once or one at a time is more efficient to avoid blocking the UI.

JavaScript UI Queue and UI Thread lanes depicted: timed code is intercalated taking turnsFigure 1 - JavaScript UI Queue and UI Thread lanes depicted: timed code is intercalated taking turns

The JavaScript code I implemented processes a bunch of questions I receive from the server to prepare them to be shown to the user. Using a AJAX GET request’s success callback I dumped all the questions (usually 100 up to more than 200 sometimes and each one formatted with the help of a ASP.NET partial view) inside a div element $("#questions") like this:

// Loading Questions for the Chapter selected...
function loadQuestions(chapterId)
{
    $("#questions").fadeOut('slow').empty();

    $.ajax({
        type: "GET",
        url: "@Url.Action(MVC.UserAssessment.ActionNames.List, MVC.UserAssessment.Name)",
        data: { assessmentId: @Model.AssessmentId, chapterId : chapterId },
        cache: false,
        success: function(questions)
        {           
            $("#questions").html(questions);
            
            setUpQuestions();
        },
        error: function()
        {
            alert("@Html.Raw(Localization.UnknownErrorAjax)");
        }
    });
}

With this I had all the questions’ HTML beautifully inserted on the page but I needed to process this HTML before showing it to the user. That’s where the setUpQuestions(); method played its role. It does the heavy lifting and was where the whole thingy just got screwed up – the browser hung from time to time while inside that method… How did I discover that the problem was inside that method? I used Firebug’s Console and the JavaScript’s Date object as shown in that O’Reilly article. In setUpQuestions I use jQuery’s find, setup form fields validation with jQuery validation plugin, disable/enable fields, apply CSS styles to the questions, etc and all of this was tackled all at once by the poor browser JavaScript engine.

To profile setUpQuestions I created a stopWatch JavaScript method that receives setUpQuestions method as the func parameter:

function stopWatch(func)
{
    var start = +new Date(), stop;

    func();

    stop = +new Date();
if (stop - start < 50)
{
//alert("Just about right.");
console.log("Just about right.");
} else
{
//alert("Taking too long.");
console.log(“Taking too long.");
} }

According to that O’Reilly article, the author recommends never letting any Javascript code execute for longer than 50 milliseconds continuously, just to make sure the code never gets close to affecting the user experience – blocking the UI thread.

When I ran the code selecting different chapters with varying number of questions I kept getting “Taking too long” and then I found where the problem was. I knew it was time to adapt the code presented in the Timed Code section of that O’Reilly article. So here it is and commented where appropriate to make understanding it a little bit easier:

/// More about it here: http://answers.oreilly.com/topic/1506-yielding-with-javascript-timers/
function
timedProcessArray(items, process, callback) { var todo = $.makeArray(items); // The first call to setTimeout() creates a timer to process the first item in the array. setTimeout(function () { var start = +new Date(); do { // Calling todo.shift() returns the first item and also removes it from the array. process(todo.shift()); } // After processing the item, a check is made to determine whether there are more items to process and if the time hasn't exceeded the threshold of 50 milliseconds while (todo.length > 0 && (+new Date() - start < 50)); if (todo.length > 0) { // Because the next timer needs to run the same code as the original, arguments.callee is passed in as the first argument. setTimeout(arguments.callee, 25); }
else { //If there are no further items to process, then a callback() function is called. if (callback) { callback(items); } } }, 25); }

This code made the whole thing fluid and now the user has a much better experience while interacting with the page despite it having a lot of form controls. What it basically does is: process a batch of items/questions and then allows the UI thread to take some processing time and then it repeats until there’s no more questions left in the todo array. The process method is actually the setUpQuestions method that gets passed as a parameter called process.

In my specific case, each question has 4 HTML input elements (input/text, select, etc). If the user selects a Manual’s Chapter to answer and this Chapter contains 230 questions for example, the <form> element will contain about 920 controls = 4 x 230.That's a lot of controls to be processed by the JavaScript code inside the setUpQuestions method. Now no matter how many controls are present in the HTML code. timedProcessArray will handle this easily allowing the UI thread to breath from time to time.

This is the modified version of the loadQuestions method that makes use of this life saving timedProcessArray method where setTimeout shines:

// Loading Questions for the Chapter selected...
function loadQuestions(chapterId)
{
    $("#questions").empty();

    $.ajax({
        type: "GET",
        url: "@Url.Action(MVC.SAvE.UserAssessment.ActionNames.Answer, MVC.SAvE.UserAssessment.Name)",
        data: { assessmentId: '@Model.AssessmentId', chapterId : chapterId, format: '@Assessment.Format.List' },
        cache: false,
        success: function(data)
        {
            var questions = $(data);

            questions.hide().appendTo("#questions");

            //stopWatch(function(){ return timedProcessArray(questions, setupQuestion, stats)});

            timedProcessArray(questions, setupQuestion);

            //stats(questions);
}, error: function() { alert("@Html.Raw(Localization.UnknownErrorAjax)"); } }); }

I didn’t pass a callback function to timedProcessArray but that’s up to you.

Hope it helps you take the most out of your highly intensive processing JavaScript code.

As a last note, with the arrival of HTML5 we now have Web Workers but browser support is still limited. Things are getting better for us developers. Smile In the near future this will be standard for sure but till then we must find a way to solve the problem with the proven tools/code. setTimeout is one of them.

Parallel LINQ (PLINQ) with Visual Studio 2010/2012 - Perf testing

On the last day of May I wrote about how to calculate prime numbers with LINQ in C#. To close that post I said that I’d use the PrimeNumbers delegate to evaluate PLINQ (Parallel LINQ) and measure the performance gains when the same calculation is done in parallel instead of in a sequential fashion.

PLINQ is LINQ executed in Parallel, that is, using as much processing power as you have in your current computer.

If you have a computer with 2 processor cores like a dual core processor you'll get your Language Integrated Query operators do the work in parallel using both cores.

Using "only" LINQ you won't get as much performance because the standard Language Integrated Query operators won't parallelize your code. That means your code will run in a serial fashion not taking advantage of all your available processor cores.

There are lots of PLINQ query operators capable of executing your code using well known parallel patterns.

After this brief introduction to PLINQ let’s get to the code.

As promised, today I show the performance gains when the PrimeNumbers delegate is run in 2 cores (parallel) instead of only 1 core (sequential).

Here’s the delegate code:

Func<int, IEnumerable<int>> PrimeNumbers = max =>
from i in Enumerable.Range(2, max - 1)
where Enumerable.Range(2, i - 2).All(j => i % j != 0)
select i;  

To make it a candidate to parallelization we must just call the AsParallel() extension method on the data to enable parallelization for the query:

Func<int, IEnumerable<int>> PrimeNumbers = max =>
from i in Enumerable.Range(2, max - 1).AsParallel()
where Enumerable.Range(2, i - 2).All(j => i % j != 0)
select i;  

I set up a simple test to measure the time elapsed when using the two possible ways of calling the delegate function, that is, sequentially in one core and parallelized in my two available cores (I have an Intel Pentium Dual Core E2180 @ 2.00 GHz / 2.00 GHz).

Let’s calculate the prime numbers that are less than 50000 sequentially and in parallel:

IEnumerable<int> result = PrimeNumbers(50000);
Stopwatch  stopWatch = new Stopwatch();

stopWatch.Start();

foreach(int i in result)
{
    Console.WriteLine(i);
}

stopWatch.Stop();

// Write time elapsed
Console.WriteLine("Time elapsed: {0}", stopWatch.Elapsed);

Now the results:

1 core
Time elapsed: 00:00:06.0252929

2 cores
Time elapsed: 00:00:03.2988351

8 cores*
Time elapsed: 00:00:00.8143775

* read the Update addendum bellow

When running in parallel using the #2 cores, the result was great - almost half the time it took to run the app in a sequential fashion, that is, in only #1 core.

The whole work gets divided into two worker threads/tasks as shown in Figure 1:

Prime Numbers PLINQ Parallel Stacks Window ( #2 cores )
Figure 1 - The Parallel Stacks window in Visual Studio 2010 ( #2 cores )

You can see that each thread is responsible for a range of values (data is partitioned among available cores). Thread 1 is evaluating the value 32983 and Thread 3 is evaluating 33073. This all occurs synchronously.

If I had a computer with 4 cores, the work would be divided into 4 threads/tasks and so on. If the time kept decreasing I’d achieve 1.5 seconds to run the app. Fantastic, isn’t it?

The new Microsoft Visual Studio 2010 (currently in Beta 2) comes with great debugging tooling for parallel applications as for example the Parallel Stacks shown in Figure 1 and the Parallel Tasks window shown in Figure 2:

Prime Numbers PLINQ Parallel Tasks Window ( #2 cores )
Figure 2 - The Parallel Tasks window in Visual Studio 2010 ( #2 cores )

This post gives you a rapid view of PLINQ and how it can leverage the power of you current and future hardware.

The future as foreseen by hardware industry specialists is a multicore future. So why not get ready to it right now? You certainly can with PLINQ. It abstracts all the low level code to get parallel and let’s you focus on what’s important: your business domain.

If you want to go deep using PLINQ, I advise you to read Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4 by Stephen Toub.

Updated on February 15, 2013

Running this same sample app on a Intel Core i7-3720QM 2.6GHz quad-core processor (with #4 cores and #8 threads) this is the result:

Time elapsed: 00:00:00.8143775

This is on a par with the #1 core and #2 cores tests shown above. The work is being divided "almost" evenly by 8 if we compare with the first benchmark ( only #1 core ).

00:00:06.0252929 / 8 = 0.7525

Of course there’s been lots of improvements between these different processor generations. The software algorithms used to parallelize the work have also been improved (now I’m running on Visual Studio 2012 with .NET 4.5). Both hardware/software specs are higher now. Nonetheless these numbers give a good insight about the performance gains both in terms of hardware and software. Software developers like me have many reasons to celebrate! Party smile

Prime Numbers PLINQ Parallel Tasks Window ( #8 threads )Figure 3 - The Parallel Stacks window in Visual Studio 2012 ( #8 threads )

If I take out the .AsParallel() operator, the program runs on a single core and the time increases substantially:

Time elapsed: 00:00:03.4362160

If compared with the #4 cores benchmark above, we have:

00:00:03.4362160 / 4 = 0.8575

0.8575 – 0.8143 = 0.0432 (no difference at all)

Note: this faster processor running on a single core has a performance equivalent to the old Intel #2 core processor. Pretty interesting.

References
Features new to parallel debugging in VS 2010
Debugging Task-Based Parallel Applications in Visual Studio 2010 by Daniel Moth and Stephen Toub

Great lecture on what to expect from the multicore and parallel future…
Slides from Parallelism Tour by Stephen Toub

PLINQ documentation on MSDN
http://msdn.microsoft.com/en-us/library/dd460688%28VS.100%29.aspx

Parallel Computing Center on MSDN
http://msdn.microsoft.com/en-us/concurrency/default.aspx

Daniel Moth’s blog
http://www.danielmoth.com/Blog/index.htm

Microsoft Visual Studio 2010
http://www.microsoft.com/visualstudio/en-us/products/2010/default.mspx