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

2/10/2013

ASP.NET MVC 4 Highlights: Bundling and Minification

By: John V. Petersen


In the first installment of this series, I explored a few of the new features in ASP.NET MVC 4, including the new default project templates, mobile templates, and display modes. Since that article, ASP.NET MVC 4 has been released to beta. For brevity’s sake, when I refer to MVC the design pattern, I’m referring to the ASP.NET implementation of the pattern. In this installment, I’m going to focus on one of MVC’s most useful features: integrated JavaScript and CSS bundling and minification.
One of the most important considerations in any Web application is the size of the content rendered to the browser. Bundling and minification handle two important tasks. First, all of the disparate JavaScript and CSS files are combined into one or more files. Second, the JavaScript and CSS code is minified by means of removing all of the carriage returns and line feeds as well as verbose variable names in favor of shorter (less verbose) alternatives.
With bundling, there are fewer resources that need to be rendered to the browser. With minification, the size of such resources is much smaller, often by as much as 70% or more!
These are not new concepts. Tools like JSMin and YUI have been around for several years. You have always been able to incorporate these bundling and minification solutions. What’s new are the integrated features baked into the MVC framework that handle these bundling and minification tasks out of the box.
Bundling and Minification “Out of the Box”
Figure 1 illustrates what you get out of the box for bundling and minification. Like all ASP.NET applications, everything starts in the Global.asax Application_Start() event handling code.
Click for a larger version of this image.

Figure 1: These code windows represent the out-of-the-box bundling and minification implementation in Global.asax and _Layout.cshtml.

In that code, there’s a call to:
BundleTable.Bundles.RegisterTemplateBundles();
The code for this method is burned into the System.Web.Optimization.dll. Figure 2 illustrates what that code looks like.
Click for a larger version of this image.

Figure 2: This code represents the out-of-the-box implementation code for RegisterTemplateBundles in System.Web.Optimization.

The code is straightforward in that bundles are created and files are added to the bundles. There are two ways to add files. One way is to refer to the file specifically. The second way is to specify a directory along with a file pattern search string. In the second method, the code can selectively traverse subdirectories to add content.
Each method has their respective pros and cons. When you specify the files, you know exactly what is getting into your bundled and minified files. This method requires more code. When you add directories, you don’t have to write as much code, but the possibility exists for unwanted code in the bundled and minified file. Choosing between adding files, directories or some combinatin thereof is a matter personal preference.
To use this functionality, there are a few concepts you must be familiar with:
  • Bundle Class: Encapsulates one or more files to be bundled and minified
  • AddFile() Method: Adds a specifically named file to the bundle
  • AddDirectory() Method: Adds files that match a file search string within the specified directory
  • Transform Property: An instance of IBundlerTransform, the object in this property performs the minification task
  • Path Property: The string that identifies the bundle within the Bundles Member in BundleTables
As you will see in a moment, as is custom in MVC, if you don’t like the default out of the box functionality in MVC, you can substitute your own functionality. First, take a look at what the default functionality gives you. Figure 3 illustrates how the disparate javaScript (JS) and Cascading Style Sheet (CSS) files are rendered to the browser.
Click for a larger version of this image.

Figure 3: The network view lists the bundled and minified CSS and JS files (with a query string parameter that is explained in the next section).

Note the file paths:
/Content/css/Content/themes/base/css/Scripts/js
These are the paths specified when the bundles were created. Figure 4 illustrates how the minified JavaScript appears.
Click for a larger version of this image.

Figure 4: This is the minified and bundled JavaScript content as rendered to the browser.

Why Is a Query String Parameter Added to the JavaScript and CSS Files?
Every time the page is refreshed, the server-side code is executed, causing the _Layout.cshtml Razor Template to evaluate. You always want the client to recognize the latest JavaScript and CSS content. If the file were always named the same, the browser may reference its cache. This behavior can always be configured at the browser level. However, that is not usually a feasible solution because it requires each computer to be specifically configured. If you have the chance to control behaviors that are essential to your applications at the server level, take it! With the addition of the query string, the client will interpret this to be a new file and therefore, rely on its cache.
You might be thinking that the call to ResolveBundleUrl is required for the bundling and minification to work. It’s not. To illustrate, I’ll replace the code for /Content/css with the following:
<link href="~/Content/css" rel="stylesheet" type="text/css" />
Figure 5 illustrates that bundling and minification still takes place. The only difference is the file name. Without the call to ResolveBundleUrl, the query string parameter is not added.
Click for a larger version of this image.

Figure 5: If the ResolveBundleUrl call is omitted, a query string parameter will not be added.

Essentially, that’s it! That’s what we get for free. But what about debugging? I don’t want my code minified in those cases. Am I stuck?
Of course not! In MVC, with almost no exception, you can override the default out-of-the-box behavior for your own behavior. In the next section, you will see how to create your own custom bundler and minification class.

Creating a Custom Bundler
If there are limitations to how the bundling and minification feature is implemented in the beta, these two would be at the top of the list:
  • The code to create the bundles is burned in a dll.
  • There is no way to make the default functionality sensitive to debug vs. release modes.
While bundling is something you probably always want, minification isn’t. While debugging, you need the unminified code to be rendered. Out of the box, using the default bundler, you cannot conditionally bundle. Let’s solve that problem now. The custom bundler code in Listing 1 solves the problem.
What’s the Second Bool Variable in AddFile()?
The second Bool Variable is the throwIfNotExist parameter. If the specified file does not exist, you can elect to have an exception thrown. In the default beta implementation, this parameter is set to false.
If you don’t want to be explicit with your files, the code could be simplified to what is illustrated in Listing 2.
The AddDirectory() method has four parameters:
  • directoryVirtualPath: the root directory used to search for files to bundle and minify
  • searchPattern: specifies the pattern to limit which files are included in the bundle
  • searchSubDirectories: if true, the process recursively searches all contained subdirectories under the directoryVirtualPath
  • throwIfNotExist: if true, the process throws an exception if the specified directoryVirtualPath does not exist
In this simplified code, every js file under /Scripts and every CSS file under /Content is included.
In both cases, a compiler directive is added to specify the transformer used to drive the minification process. Out of the box, there is a class called NoTransform. As the name implies, this class does not minify. You need such a class because the bundler instance requires a transformer:
var bundle = new Bundle("~/Scripts/js", jstransformer);
The bundler does not care what the transformer does. As long as it gets an instance that conforms to IBundleTransformer, the Bundle instance will be happy. Listing 3 shows what the NoTransform class is.
I Like the YUI Minifier; Can I Use That?
The nice thing about the way bundling and minification was implemented in ASP.NET MVC is that you don’t have to give up using utilities that you are currently using. The implementation works very much like the way IDependencyResolver works. In Version 3, an inversion of control container adapter was added to abstract away the details of any specific IoC container from the framework. The bundling and minification process illustrated here works very much the same way. The process begins with creating a custom instance of IbundlerTransform, as shown in Listing 4.
To take advantage of the YUICompressor Transform Class, the code in Listing 2 that loads the proper transformer must be changed to the following:
bool isDebug;
#if DEBUG isDebug = true; #endif
if (isDebug) { jstransformer = new NoTransform("text/javascript"); csstransformer = new NoTransform("text/css"); } else { jstransformer = new YUITransform(contentType.javascript); csstransformer = new YUITransform(contentType.css); }
I changed the debug-checking process slightly, because I am gradually moving to a solution that is testable. The next step involves creating a custom abstraction over the base Bundle Class that begins to abstract away the details of which files to add. I’ll leave that exercise to you to explore.
File Ordering within a Bundle
By default, JavaScript files are first ordered alphabetically within a bundle. Then, the files are restacked around known libraries. For example, jQuery - related files occur first in the bundle. Within the jQuery group, the files are sorted alphabetically. For CSS files, the files are first sorted alphabetically. Then, if the files reset.css or normalize.css exist, that content appears at the top of the bundled CSS file. Figure 6 illustrates this behavior in the bundled CSS file. Like everything else, this behavior is completely customizable. The Bundle Class has an Orderer property that conforms to the IBundleOrderer interface.
Click for a larger version of this image.

Figure 6: The Bundle.Orderer Property controls how files are ordered within the bundle.

Conclusion
With each release, the ASP.NET MVC Framework gets better and better. Bundling and minification is an essential process that needs to be in every production Web app that relies on significant JavaScript and CSS resources. Out of the box, the functionality is pretty good. There are, however, some missing pieces. Fortunately, the process was designed with customization and extensibility in mind. With a little effort, it was easy to toggle minification based on whether or not the application was being run under debug mode.
One final point, this article was based on beta software. The usual disclaimer applies - this functionality may change when the product is released to manufacturing (RTM).
John V. Petersen
&

SPONSORED SIDEBAR: Learn ASP.NET MVC in a Day!

ASP.NET is one of the world’s most popular Web development environments. We can help you with all ASP.NET projects as well as related technologies, such as HTML (4 and 5), JavaScript, jQuery, CSS, AJAX, services, and many more. (Learn more from www.codemag.com/consulting.)
That’s why CODE Training is offering a full day of training in ASP.NET MVC from CODE Consultants, experts in Web development. CODE Training and EPS Software will be holding an intensive one-day lecture, June 5, 2012, on ASP.NET MVC specifically designed for developers of business applications. Learn how, when and why to use ASP.NET MVC for the best result in your projects. Only $399!
Visit www.codemag.com/training to find out a little bit more about the class, or send an e-mail to info@codemag.com for more information.


Listing 1: Custom bundler class
using System;using System.Linq;using System.Web.Optimization;
namespace MVC4BundleUI{ public class MyBundler { public static void init() { IBundleTransform jstransformer; IBundleTransform csstransformer;
#if DEBUGjstransformer = new NoTransform("text/javascript"); csstransformer = new NoTransform("text/css"); #else jstransformer = new JsMinify(); csstransformer = new CssMinify(); #endif
var bundle = new Bundle("~/Scripts/js", jstransformer);
bundle.AddFile("~/Scripts/jquery-1.6.2.js", true); bundle.AddFile("~/Scripts/jquery-ui-1.8.11.js", true); bundle.AddFile("~/Scripts/jquery.validate.unobtrusive.js", true); bundle.AddFile("~/Scripts/jquery.unobtrusive-ajax.js", true); bundle.AddFile("~/Scripts/jquery.validate.js", true); bundle.AddFile("~/Scripts/modernizr-2.0.6-development-only.js", true); bundle.AddFile("~/Scripts/AjaxLogin.js", true); bundle.AddFile("~/Scripts/knockout-2.0.0.debug.js", true);
BundleTable.Bundles.Add(bundle);
bundle = new Bundle("~/Content/css", csstransformer);
bundle.AddFile("~/Content/site.css", true);
BundleTable.Bundles.Add(bundle);
bundle = new Bundle("~/Content/themes/base/css", csstransformer);
bundle.AddFile("~/Content/themes/base/jquery.ui.core.css", true); bundle.AddFile("~/Content/themes/base/jquery.ui.resizable.css", true); bundle.AddFile("~/Content/themes/base/jquery.ui.selectable.css",true); bundle.AddFile("~/Content/themes/base/jquery.ui.accordion.css",true); bundle.AddFile("~/Content/themes/base/jquery.ui.autocomplete.css",true); bundle.AddFile("~/Content/themes/base/jquery.ui.autocomplete.css", true); bundle.AddFile("~/Content/themes/base/jquery.ui.dialog.css",true); bundle.AddFile("~/Content/themes/base/jquery.ui.slider.css", true); bundle.AddFile("~/Content/themes/base/jquery.ui.tabs.css", true); bundle.AddFile("~/Content/themes/base/jquery.ui.datepicker.css",true); bundle.AddFile("~/Content/themes/base/jquery.ui.progressbar.css",true); bundle.AddFile("~/Content/themes/base/jquery.ui.theme.css", true);
BundleTable.Bundles.Add(bundle); } }
}

Listing 2: Simplied custom bundler code using the AddDirectory() method
public class MyBundler { public static void init() { IBundleTransform jstransformer; IBundleTransform csstransformer;
#if DEBUGjstransformer = new NoTransform("text/javascript"); csstransformer = new NoTransform("text/css"); #else jstransformer = new JsMinify(); csstransformer = new CssMinify(); #endif
var bundle = new Bundle("~/Scripts/js", jstransformer); bundle.AddDirectory("~/Scripts/","*.js",true, true); BundleTable.Bundles.Add(bundle);
bundle = new Bundle("~/Content/css", csstransformer); bundle.AddDirectory("~/Content/", "*.css", true, true);
BundleTable.Bundles.Add(bundle); } }

Listing 3: NoTransform transformation Class
public class NoTransform : IBundleTransform { readonly string _contentType;
public NoTransform(string contentType) { this._contentType = contentType; }
public void Process(BundleContext context, BundleResponse response) { response.ContentType = this._contentType; } }

Listing 4: YUI Compressor transformation class
using System.IO;using System.Web.Optimization;using Yahoo.Yui.Compressor;
namespace Bundler.Utilities{ public enum contentType { javascript, css }
public class YUITransform : IBundleTransform { readonly string _contentType = string.Empty;
public YUITransform(contentType contentType) { if (contentType == contentType.css) { this._contentType = "text/css"; } else { this._contentType = "text/javascript"; } }
public void Process(BundleContext context, BundleResponse bundle) { bundle.ContentType = this._contentType;
string content = string.Empty;
foreach (FileInfo file in bundle.Files) {
using (StreamReader fileReader = new StreamReader(file.FullName)) { content += fileReader.ReadToEnd(); fileReader.Close(); }
}
bundle.Content = Compress(content); }
string Compress(string content) { if (_contentType == "text/javascript") { return JavaScriptCompressor.Compress(content); } else { return CssCompressor.Compress(content, CssCompressionType.StockYuiCompressor); } } }}

3/17/2012

Optimizing C# for XAML Platforms

Georgi Atanasov and Tsvyatko Konov
When working with the C# development language, individual developers often find a process that works for them and stick to it. After all, if the code passes all our tests, we can ship it, right? Well, no. Releasing products in today’s complex programming landscape isn’t that simple. We think developers need to revisit some of the standard decisions we make about Extensible Application Markup Language (XAML) concepts such as dependency properties, LINQ and the layout system. When we examine these aspects from a performance perspective, various approaches can prove questionable.
By exploring dependency properties, LINQ performance and the layout system through some code examples, we can see exactly how they work and how we can get the best performance out of our applications by rethinking some common assumptions.

The Problem with Dependency Property Look-Up Time

DependencyProperty and DependencyObject are the fundamentals on which Windows Presentation Foundation (WPF), Silverlight and XAML are built. These building blocks provide access to critical features such as styling, binding, declarative UI and animation. In a typical program, we use them all the time. Every single bit of such high-end functionality comes at a price measured in performance, however, be it loading time, rendering speed or the application’s memory footprint. To support framework functionality that includes default values, styles, bindings, animations or even value coercion in WPF, the property system backing them up needs to be more complex than standard CLR properties.
The following steps occur during a DependencyProperty effective value look-up:
  • The structure holding the data for the specified property is retrieved from the property store.
  • Once the structure is retrieved, its effective value is evaluated -- is it a default value, a style, a binding or an animated value?
  • The final effective value is returned.
Figure 1 shows some measurements (in milliseconds) of CLR properties and DependencyProperty usage.
100,000 IterationsCLR PropertiesDependencyProperty
Set different values3 ms1062 ms
Set same value3 ms986 ms
Get value3 ms154 ms
Figure 1 DependencyProperty Get/Set Measurements
Note All measurements were performed on the Silverlight for Windows Phone platform, on a Samsung Omnia 7 device. We used a mobile device because of its lower hardware resources, where differences are more distinct.
Figure 2 shows the class used to perform the tests.
Figure 2 Simple “Control” Inheritor
  1. public class TestControl : Control
  2. {
  3.     public static readonly DependencyProperty TestIntProperty =
  4.         DependencyProperty.Register("TestInt"typeof(int), typeof(TestControl), new PropertyMetadata(0));
  5.     public int TestInt
  6.     {
  7.         get
  8.         {
  9.             return (int)this.GetValue(TestIntProperty);
  10.         }
  11.         set
  12.         {
  13.             this.SetValue(TestIntProperty, value);
  14.         }
  15.     }
  16. }
This test is the simplest one possible, without any styles, bindings or animations applied. If you try the same scenario on a ListBox, you’ll see even bigger numbers. It demonstrates that DependencyProperty usage is heavier and implies that performance pitfalls can result in enormous overhead. In applications that use extensive looping, this performance hit is even more apparent.

Solving the Dependency Property Look-Up Time Problem

The challenge is to keep all the value provided by the dependency property system and to improve the look-up performance at the same time. Two important yet simple optimizations can help improve the overall application performance.

Cache a DependencyProperty effective value in a member variable for later use

Figure 3 shows an extended version of the TestControl class.
Figure 3 Simple “Control” Inheritor with a Cached Property Value
  1. public class TestControl : Control
  2. {
  3.     public static readonly DependencyProperty TestIntProperty =
  4.         DependencyProperty.Register("TestInt"typeof(int), typeof(TestControl), new PropertyMetadata(0, OnTestIntChanged));
  5.     private int testIntCache;
  6.     public int TestInt
  7.     {
  8.         get
  9.         {
  10.             return this.testIntCache;
  11.         }
  12.         set
  13.         {
  14.             if (this.testIntCache != value)
  15.             {
  16.                 this.SetValue(TestIntProperty, value);
  17.             }
  18.         }
  19.     }
  20.     private static void OnTestIntChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  21.     {
  22.         (d as TestControl).testIntCache = (int)e.NewValue;
  23.     }
  24. }
By adding a handler to listen for a change in the TestInt property and caching it in a field and in the getter, we return this cached field. When setting the value, we also check whether the value is the same -- if it’s not, the SetValue method isn’t called. The results when we perform the above measurements with the simple changes are shown in Figure 4.
100,000 IterationsCLR PropertiesDependencyProperty
Set different values3 ms1062 ms
Set same value3 ms4 ms
Get value3 ms3 ms
Figure 4 DependencyProperty Get/Set Measurements Using the Class from Figure 3
Only five lines of additional code resulted in a significant optimization. This optimization comes at a price, however. You sacrifice memory footprint (adding four more bytes to the object’s size with this additional field) for the sake of performance. The user probably won’t notice a slightly larger memory footprint but would definitely be aware of slower performance. The developer is responsible for evaluating the impact of either approach. If you have many dependency properties within your classes and you create many instances of these classes, more bytes within a single object can become a problem. If you use DependencyProperty sparingly, you don’t need to cache its effective value in a field.
Note Be careful when adding a Changed handler for a property. It will force the underlying framework to synchronize the property value with the UI thread, which can downgrade performance for properties whose values are animated. Also keep in mind that the “if” check in the property setter won’t work with bindings because the framework internally uses the SetValue(TestIntProperty, value) method rather than the property setter.

Cache a DependencyProperty effective value outside a loop

The example in Figure 3 works because we have our own class that we can modify as desired. But what if we have to use a DependencyProperty from an external library and we don’t have access to its source? We can handle this with another simple yet efficient optimization. Consider the following code:
  1. for (int i = 0; i < 100000; i++)
  2. {
  3.     if (this.ActualWidth == i) // "this" refers to a PhoneApplicationPage instance
  4.     {
  5.         // perform some action
  6.     }
  7.     else
  8.     {
  9.         // perform other action
  10.     }
  11. }
Do you see something that can be written more efficiently? Here is a slightly modified version of the preceding loop:
  1. double actualWidth = this.ActualWidth; // "this" refers to a PhoneApplicationPage instance
  2. for (int i = 0; i < 100000; i++)
  3. {
  4.     if (actualWidth == i)
  5.     {
  6.         // perform some action
  7.     }
  8.     else
  9.     {
  10.         // perform other action
  11.     }
  12. }
With this modified approach, we look up the value of the property only once and then use the value, cached in a local variable, to perform the “if” clause. The optimized results are shown in Figure 5.
100,000 IterationsLoop Time Elapsed
Before optimization750 ms
After optimization4 ms
Figure 5 Comparison of Loop Performance
Pretty impressive! This optimization is valid only if the ActualWidth value isn’t changed during the loop. If there’s a condition that could change this value, you need to update the variable upon the change or look it up every time if you don’t know when the change will occur.

Some final thoughts about DependencyProperty caching

Dependency properties are great, and they help make XAML the powerful framework it is. But don’t forget that in some cases they can degrade performance significantly. Always keep in mind the overhead the dependency property system brings into setting/getting a value and use the preceding tricks when appropriate. Estimate which properties need to be dependency ones and which can be simple CLR properties. For example, if you have a getter-only property, you don’t need to register a DependencyProperty for it, but rather a CLR property, and to raise the PropertyChanged notification to enable bindings.

Efficient Looping With -- and Without -- LINQ

Since LINQ was first released in 2007 as part of .NET Framework 3.5, developers seldom write their own loops anymore, relying on LINQ instead. LINQ is powerful, and its beauty is that it can be executed against different providers, such as Microsoft SQL Server, in-memory objects, XML or even your own custom provider implementing the IQueryable interface. We love LINQ—its framework libraries often spare us from writing many lines of code. Sometimes, however, we still have to write our own loops for the sake of performance. By properly estimating our algorithm complexity, we can recognize whether we can use LINQ or need our own loops.
For example, we can write code to solve a simple problem. Let’s say we need to find the minimum, maximum and average of an array of integers. As shown in Figure 6, using LINQ makes it simple and clean.
Figure 6 Finding Min/Max/Average Using LINQ
  1. private double[] FindMinMaxAverage(List<int> items)
  2. {
  3.     return new double[] { items.Min(), items.Max(), items.Average() };
  4. }
Using our own loop, the code would look like Figure 7. (Yes, there is much more code.)
Figure 7 Finding Min/Max/Average of a Sequence Using a Custom Loop
  1. private double[] FindMinMaxAverage(List<int> items)
  2. {
  3.     if (items.Count == 0)
  4.     {
  5.         return new double[] { 0d, 0d, 0d };
  6.         // we may throw an exception if appropriate
  7.         // throw new ArgumentException("items array is empty");
  8.     }
  9.     double min = items[0];
  10.     double max = min;
  11.     double sum = min;
  12.     for (int i = 1; i < items.Count; i++)
  13.     {
  14.         if (items[i] < min)
  15.         {
  16.             min = items[i];
  17.         }
  18.         else if (items[i] > max)
  19.         {
  20.             max = items[i];
  21.         }
  22.         sum += items[i];
  23.     }
  24.     return new double[] { min, max, sum / items.Count };
  25. }
When you compare the two routines, you can see the time advantage of our loop over LINQ, as shown in Figure 8.
MethodTime Elapsed
LINQ60 ms
Loop20 ms
Figure 8 Execution Time of Methods in Figure 6 and Figure 7
Are you surprised by the results? The LINQ implementation is highly efficient, and we couldn’t write a better performing loop to find the minimum of a sequence. But finding the minimum, maximum and average requires the LINQ approach to loop through the entire sequence three times. So the loop we wrote is three-times faster because of the difference in complexity -- the complexity of our loop is O (n) while the complexity of the method that uses LINQ is O (3n).

To LINQ or Not To LINQ

Figure 6 is a simple example that demonstrates the importance of complexity estimation in writing efficient code. We should always evaluate different solutions, analyze their pros and cons and decide which one to use in a particular context.
For example, if this method is used once or twice in the application, we obviously don’t need to write more code. But if it is used extensively, for example, in a charting engine, we should definitely opt for the second solution because it improves performance 200% per single method call.
A LINQ extension method generally has O (n) or O (nlogn) complexity, depending on the method. It also has some optimizations. For example, the Count method checks whether the sequence is ICollection; if yes, it returns its Count property directly, making the complexity a constant -- O (1). Because of its lazy initialization, it can merge queries, reducing the complexity.
OrderBy uses the same custom QuickSort implementation as in the List<T>.Sort method. We tried our own GroupBy implementation and gained only several milliseconds compared to LINQ. In this case, why would we bother writing our own grouping algorithm when LINQ already implements one efficiently?  You always need to measure the execution time of the methods you write, estimate how often the methods will be used and figure out whether you can write a better implementation.
A more complicated example with some XAML code illustrates where using LINQ adds enormous complexity. Consider the code in Figure 9.
Figure 9 Sample Code with Extensive LINQ Usage
  1. private void UpdateRelatedEntries(List<LinqViewModel> items)
  2. {
  3.     foreach (LinqViewModel item in items)
  4.     {
  5.         item.Related = items.Where(s => s != item)
  6.                             .OrderBy(s => s.Score).Reverse()
  7.                             .Take(5)
  8.                             .ToList();
  9.     }
  10. }
Can you guess the complexity of this “simple” code? Let’s analyze it:
foreach -> O (n)
  • Items.Where(s => s != item) -> O (n - 1)
  • OrderBy(s => s.Score) - > O ((n – 1)log(n – 1)) (quicksort)
  • Reverse() -> constant, will be merged with Take
  • Take(5) -> constant
  • ToList() -> constant since Array.Copy is used internally and only the time for memory allocation is spent
The overall complexity is O (n * ((n – 1) + (n – 1) * log (n – 1)). This approach provides more than a quadratic complexity, which is unacceptable and can be a show stopper in your application.
Now imagine adding a Select clause with some additional LINQ methods within its body. The complexity would be near cubical. Measuring the execution time of this method with our 100,000 entries results in a mind-blowing five-minute (and still running) freeze on our Omnia 7 screen. Some of you might be thinking that it isn’t realistic to experiment with 100,000 entries on a mobile device. And you’re right -- that’s a lot of data even for a desktop app. But the point remains that the complexity of this algorithm increases performance time exponentially.
Figure 10 shows the results from tests using 100, 1000 and 5000 items -- numbers more realistic for a mobile app.
ItemsTime Elapsed
10045 ms
10004222 ms
5000138829  ms == ~2.5 minutes
Figure 10 Execution Times for the Method in Figure 9

Optimizing the loop

The algorithm definitely needs to be improved. For starters, why do we need to do an expensive sort operation upon each iteration? We could just sort the items in descendant order once, outside the loop. As shown in Figure 11, this approach automatically removes the inner OrderBy and Reverse calls.
Figure 11 Optimization of the Method in Figure 9
  1. private void UpdateRelatedEntries(List<LinqViewModel> items)
  2. {
  3.     List<LinqViewModel> sortedDescendantItems = items.OrderBy(item => item.Score).Reverse().ToList();
  4.     foreach (LinqViewModel item in items)
  5.     {
  6.         item.Related = sortedDescendantItems.Where(s => s != item)
  7.                             // .OrderBy(s => s.Score).Reverse()
  8.                             .Take(5)
  9.                             .ToList();
  10.     }
  11. }
You might be wondering why we copy the result of the query in a list. The reason is a bit tricky. Because LINQ uses lazy initialization, the quick sort of the OrderBy call is performed when the iteration starts. If the result isn’t copied to a list (making the query iterate once), the quick sort is performed upon each iteration, even when the query is made outside the loop. Another consequence of lazy initialization is that the Take(5) and Where(…) clauses will be merged, making the overall execution time inside the loop constant.
Figure 12 shows what happens when we do the measurements with this optimization.
ItemsTime Elapsed
10013 ms
100029 ms
5000131 ms
Figure 12 Execution Times of the Method from Figure 11
That’s much better -- from an exponential complexity, we made it to ~O (nlogn). This is considered good in computer science. Still, if this were our code, we wouldn’t use LINQ in the loop’s body. Instead, we would write our own inner loop, add the first five items and then break the loop, as shown in Figure 13.
Figure 13 The Method from Figure 9 Implemented Using our Own Loops
  1. private void UpdateRelatedEntries(List<LinqViewModel> items)
  2. {
  3.     List<LinqViewModel> sortedDescendantItems = items.OrderBy(item => item.Score).Reverse().ToList();
  4.     for (int i = 0; i < items.Count; i++)
  5.     {
  6.         LinqViewModel item = items[i];
  7.         List<LinqViewModel> relatedItems = new List<LinqViewModel>(8);
  8.         for (int j = 0; j < sortedDescendantItems.Count; j++)
  9.         {
  10.             if (sortedDescendantItems[j] == item)
  11.             {
  12.                 continue;
  13.             }
  14.             relatedItems.Add(sortedDescendantItems[j]);
  15.             if (relatedItems.Count == 5)
  16.             {
  17.                 break;
  18.             }
  19.         }
  20.         item.Related = relatedItems;
  21.     }
  22. }
Time for measurements again. The results are shown in Figure 14.
ItemsTime Elapsed
1006 ms
100013 ms
500057 ms
Figure 14 Execution Times of the Method from Figure 13
Ah, the good old-fashioned loop -- nothing is faster. If you prefer LINQ and plan to use the method from Figure 9 only rarely, you’ll be fine. If you plan to use it extensively, however, you’re better off writing your own loop, which will complete its work in half the time.

Some final thoughts on looping with LINQ

LINQ is efficient, saves you from writing a lot of code, and is neat, clean and easily read. It also allows you to execute queries against different providers. But as demonstrated in Figure 9, it can also add undesired complexity and degrade performance. You’ll get into trouble if you think of LINQ as a single method call with constant execution time rather than the shortcut to different algorithms that it is. The complexity involved in looping is what matters. In some cases, you still need to write your own loops instead of relying on LINQ.

Working with the XAML Layout System

To create the page layout in the XAML layout system, each container is measured and then arranged. While being measured, each container recursively calls its children and asks for the desired size. Then all the elements are arranged on the available rectangle. This process happens through the corresponding methods MeasureOverride and ArrangeOverride.
Before going further into the layout system code, let’s look at the main layout panels, their capabilities and their implementation. Canvas, StackPanel and Grid are some of the layout panels you’ll be working with most often. Let’s also consider virtualization. We’ll show you how to use one of its applications in the face of VirtualizingStackPanel.
Canvas
Canvas is used to arrange items on one panel based on absolute offset. Canvas performs better than Grid and StackPanel because each child measure doesn’t rely on the desired size of other children. In most cases, however, the differences among Canvas, Grid and StackPanel are relatively small since each measures all their children. This control is suitable when its children rely only on its absolute position (e.g., simple designer/drawing tool). By default, children don’t rely on each other for their position. If the scenario requires children to depend on each other for their size and translate transform is applied to mimic a grid/list-like layout, you should consider other layout panels or custom implementation.
StackPanel
StackPanel is used to arrange child elements into a line that can be oriented horizontally or vertically. It provides functionality common for lists. StackPanel measures all items even if some of them aren’t visible. If you need the panel of an itemsControl to display many elements, consider using virtualized panels instead of StackPanel.
Grid
Grid is a panel used to arrange elements in a grid-like layout with both column and row definitions. Compared to Canvas and StackPanel, Grid has a heavier measure cycle. It is the default panel used for new UserControls and Windows.
Virtualization (VirtualizingStackPanel)
In most scenarios where performance is an issue, panels are used within ItemsControl for realizing multiple items. In these situations, the best approach is to provide a panel that supports virtualization, such as VirtualizingStackPanel. With this approach, the elements realized in the visual tree are limited to the items currently visible, cutting the time for measure/arrange.

Comparing the panels

We can create a real scenario in which panels are used as the container for an ItemsControl such as ListBox. Results will be reviewed for loading. The measurements show the duration of the layout cycle (Arrange + Measure). The code involved will be similar to that shown in Figure 15.
Figure 15 Code for Canvas, StackPanel, Grid and VirtualizingStackPanel
Canvas
<UserControl.Resources>
       <Style TargetType="ListBoxItem">
              <Setter Property="Canvas.Left" Value="10"></Setter>
       </Style>
</UserControl.Resources>
<ListBox  ItemsSource="{Binding Data}">
       <ListBox.ItemsPanel>
              <ItemsPanelTemplate>
                    <Canvas>
                    </Canvas>
              </ItemsPanelTemplate>
       </ListBox.ItemsPanel>
</ListBox>
StackPanel
<ListBox  ItemsSource="{Binding Data}">
       <ListBox.ItemsPanel>
              <ItemsPanelTemplate>
                    <StackPanel>
                    </StackPanel>
              </ItemsPanelTemplate>
       </ListBox.ItemsPanel>
</ListBox>
Grid
  1. <UserControl.Resources>
  2.        <Style TargetType="ListBoxItem">
  3.               <Setter Property="Grid.Column" Value="1"></Setter>
  4.        </Style>
  5. </UserControl.Resources>
  6. <ListBox  ItemsSource="{Binding Data}">
  7.        <ListBox.ItemsPanel>
  8.               <ItemsPanelTemplate>
  9.                     <Grid>
  10.                            <Grid.ColumnDefinitions>
  11.                                   <ColumnDefinition></ColumnDefinition>
  12.                                   <ColumnDefinition></ColumnDefinition>
  13.                            </Grid.ColumnDefinitions>
  14.                     </Grid>
  15.               </ItemsPanelTemplate>
  16.        </ListBox.ItemsPanel>
  17. </ListBox>
VirtualizingStackPanel
<ListBox  ItemsSource="{Binding Data}">
       <ListBox.ItemsPanel>
              <ItemsPanelTemplate>
                    <VirtualizingStackPanel>
                    </VirtualizingStackPanel>
              </ItemsPanelTemplate>
       </ListBox.ItemsPanel>
</ListBox>
The results (in seconds) are shown in Figure 16.
Panel Type100 Items1000 Items5000 Items
Canvas0.7254.728.95
StackPanel0.76454.68133329.597
Grid0.7374.812529.694
VirtualizingStackPanel0.5960.5676670.5965
Figure 16 Execution Times of Panels in Figure 15
As you can see, UI virtualization can greatly increase the performance. The logic involved in handling a custom layout is insignificant when compared to the overhead of measuring down the entire visual tree.

General suggestions for custom panels

When the default panels don’t meet our performance or behavior needs, we often create our own custom panels. Here are some suggestions to consider when creating custom panels:
  • Use virtualization when the scenario allows because it can drastically reduce load/response time.
  • Use InvalidateMeasure wisely. It triggers a layout update to the element and all its children. It also triggers both Measure and Arrange cycles.
  • When implementing custom panels, be careful using layout-specific properties, such as ActualWidth, ActualHeight, Visibility and so on. They can cause a LayoutCycleException.
  • When displaying hierarchical UI elements, consider arranging the items in one container. Using nested UI Elements/Panels increases the measure cycle time because unmanaged code is called in each nested level.

Conclusion

As you have seen, taking a closer look at the code you’re using in your XAML applications can help you make some changes to your usual coding processes that can enhance performance throughout your applications. If you understand the complexity of the dependency property system, you can optimize your code for faster retrievals. If you know exactly how LINQ uses collections, you can make decisions that lead to faster and more efficient loops. Finally, if you’re aware of how the layout system operates and how to optimize custom controls when you need them, you can create more responsive XAML applications.