Don’t Fight The Framework – Strategies to optimise Sitecore MVC Performance

After now a couple of weeks of performance optimisations on Sitecore 7.2, I thought I would detail a little bit about ways you can optimise the performance of your MVC based Sitecore solutions.

Before I go any further with this post, I would like to make it clear that I have actively engaged with Sitecore UK and Sitecore support, sent them all of my findings and made performance improvement suggestions based upon my findings. These will hopefully benefit us all in future updates and if you come to the load testing / performance optimisation phase of your project, I would highly recommend you do the same.

Below you will find a summary of the results of my findings during performance and load analysis on Sitecore. For one of my latest builds for a client, we have pretty much followed as close to best practice as we possibly could (including a site review by Sitecore themselves and actions out of it as such), we underwent load testing on the first generation of the clients new site. The site did perform to the acceptance criteria of approximate 12,000 requests a minute that is required for the site at peak time – but not by much. It also VASTLY outperformed it’s predecessor which was a Sitecore site built by a reputable UK Sitecore partner. Since the results were close to the limit though (and to allow for overhead to allow future growth) I found myself having to analyse the performance of various elements of our solution and subsequently of Sitecore itself in order to gain the maximum out of our build.

I thought I would pass on my findings to help others to get the most out of their Sitecore MVC solutions.

During my analysis, I found that by far the biggest overhead of our site was in the rendering pipelines – not entirely unexpected I will grant you, but it did lead to investigating what I could do to maximise the performance of Sitecore.

The results are from a vanilla Sitecore build with as little code as I could get away with.

Rendering performance in terms of order:

I decided to render a view like this – deliberately keeping the work done by the view to a minimum so I could concentrate on Sitecore:

<div class="something">
	<p><span>Hello World</span></p>

Static Bindings:

I started by creating pages with multiple static bindings (20 to be exact) and multiple ‘renderings’ using simple @Html.Partial(“path to my view”).

  1. @Html.Partial – Fastest, actually not as inflexible as you think since you can ‘inject’ a property from a model as a data source. There is no caching though.
  2. @Html.Sitecore.Rendering() – Not far off @Html.Partial, trickier to add the model / datasource

My findings in performance tests on vanilla Sitecore installations was that the static rendering pipeline in sitecore is pretty close to native MVC performance. Obviously we were never expecting exactly the same, but it was encouraging to see.

Dynamic Bindings

I then moved on to looking at dynamically bound renderings. I have always known there is an overhead for using these, in webforms it never seemed to have been that costly. Here is the order in which they performed during my tests.

  1. Dynamically bound view renderings without a model
  2. Dynamically bound view renderings with a model
  3. Controller Renderings

In this case I started off with 10 x dynamically bound view renderings. I then stepped this up to 15, 20 and 25 renderings on individual pages. For the purposes of the tests I did not enable caching on these renderings to test the functionality. The reason for this was simple. Under load the site has periods where the cache cycles out and has to reload the rendering from scratch, during this period, the site is at the mercy of the original code and therefore (in my humble opinion) should be optimised in this state during your first round of performance improvements. Once again, in my humble opinion, if you are reliant on your page caching then you are possibly setting yourself up for a fall later on. I believe caching should enhance an already quick implementation.

During my test cycles I tested 75 and 150 users hitting the pages with 500ms delays between requests. I found that the level of throughput drops by approximately 15% for 15 renderings, 33% for 20 and nearly 50% for 25. I also noted that on my test environment the CPU usage quickly became the limiting factor with all but the 10 rendering tests hitting the CPU hard.

I then repeated this test with views containing a very simple model with a hard coded property, I found (which I felt was reasonably obvious before I started) that the performance dropped further still. In my developments, I use models a lot since I like razor and MVC, so this was something I have had to address.

I then tested with 20 dynamically bound controller renderings (using the same .cshtml). The throughput dropped a further 17% compared with their view rendering equivalent.

Caching Saves Your Bacon Though Right?

Well – in short, as always – kinda… I continued my analysis to investigate the effect of caching. I found that it does alleviate some of the problem in that the performance does improve (approximately 17% over its non-cached counterpart), it was nowhere near the near flatline cpu usage I would be expecting or would have hoped for. CPU usage was still the limiting factor – again – particularly on higher numbers of dynamic renderings.

Performance Tips

With the findings above in mind, I thought that I would detail some recommendations based upon them.

As per Sitecore best practice (one I disagree with I might add), it is definitely recommended in MVC in particular to statically bind whatever you can. I personally think this is a shame and something I hope I can help Sitecore to rectify in future versions as I think it’s one of the greatest strengths of Sitecore. But since we are dealing with the world as it is today…

Consider using an ORM tool such as Glass Mapper – Sitecore does not allow you to specify a controller for your layout. This in its own right kind of forces you into using a statically bound renderings. This in turn causes you an issue since you cannot then specify a datasource for your rendering. Because Glass Mapper hooks into the GetModel pipeline, it allows you to create a custom model for your layouts, which in turn allows you to specify the model using the necessary @Html.Partial() overload. This does present you with a small issue in you have to then cache sections of your model if you don’t want to execute all of it every time, but publish aware (or not) caches should be a part of every Sitecore developers arsenal.

Another potential option is to consider grouping your renderings, for example – if you have a product details page / home page made up of multiple renderings that don’t require specific datasources (or if you can group the datasources together beneath an item and provide this grouped datasource). This reduces the number of times you call in both dynamic renderings and potentially use of models. In tests 4 x dynamic renderings with 5 statically bound renderings on each performed far better than if it had been dynamically bound.

Consider @Html.Partial instead of @Html.Sitecore().Rendering(). You have to make the decision based on whether you are caching etc, but you might consider occasions (particular when you are using dynamic renderings) that you can cache a grouped rendering.

Consider carefully where / when to allow Dynamic Placeholders since this could seriously affect performance.

I will continue to engage with Sitecore on improving dynamically bound renderings. Early non-production patches for pipelines I have been experimenting with yielded performance within about 5% of their equivalent statically bound renderings when performed against pages with 20 x renderings, furthermore, outperformed the original code by 50% in terms of throughput. I am sure there is more to be gained beyond this.

2 thoughts on “Don’t Fight The Framework – Strategies to optimise Sitecore MVC Performance

  1. Hi Nat, great post!

    We have observed similar overheads when testing our highly granular component-oriented architectures with Sitecore MVC and SCORE ( Dynamic binding of renderings does add up under load, no question about it. I don’t think Dynamic Placeholders themselves make it worse – it’s the number of components that ultimately brings the overall throughput down. There are multiple variables – your __Renderings XML becomes larger (from components and localized placeholder settings), more mvc.renderPlaceholder and mvc.renderRendering cycles to go through, the Model.Item (aka Rendering.Item) doing a lot of looking for the right item to pass into your view, etc.

    That said, my recommendation isn’t to statically bind renderings or use @Html.Partial(). That technique kind of obsoletes Sitecore. Especially the marketing automation part of it, right? I believe the true long term answer is still caching. A smarter and better caching that what’s currently build into Sitecore but still output caching. Nothing is free and the flexibility that a CMS promises and delivers on with the highly-granular component architecture can’t match the performance of the same end result (hard)coded in your favorite web framework. Sure we can optimize the mvc.* rendering pipelines – and we must, but the difference between that and everything hardcoded will never be negligible under load.

    I am currently exploring this exact area for the upcoming update to our SCORE framework. Our mission is to enable these beautiful highly granular component-oriented architectures and make it viable for a variety of context, including folks like high traffic sites with big holiday spikes (retail went ecommerce, for example).

    Anyway. Great article. Enjoyed reading and sharing it with other Sitecore geeks 🙂 Thanks!

    • Thanks for the response Pavel,

      My investigation has shown the CPU binding still occurs even with output caching enabled which is also in line with what you are saying about improving the caching and optimising the mvc pipelines.

      I agree that I would never choose not to use @Html.Partial or Static binding for the most part at all (and never have until this latest build), however if you want to increase the throughput for your website as it stands, your choices are that or scale sideways. I am also only advocating it in line with Sitecore’s best practice (which as previously stated – I do not agree with) for the likes of header / footer / scripts that appear on every page.

      Dynamic placeholders only make it worse in so much as it encourages the addition of component reuse on the page (as many developers do with grids / column controls), it also encourages more dynamically bound renderings which as you rightly point out are much of the problem.

      Glad you enjoyed.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s