Speed and Performance
I started this post as a talk on how to improve the development experience when creating Sitefinity sites. But it has now turned into more of a on going list of performance improvements and checks for both development and production. The suggestions are in no particular order and I haven't added images and long explanations so as to keep the ideas in one place. Some of these may or may not be appropriate for your site and environment but all should be considered when looking at improving your production or development environments. I encourage you to let me know any others that you believe are helpful.
The first thing to do is to upgrade to the latest release of Sitefinity. The application initialisation and performance of 9.2 is far superior to that of 7.3 for example and the Sitefinity team are constantly ensuring that startup times are never any worse and in most major releases are improved with each release.
But the truth remains that Sitefinity is built on ASP.NET and some of the performance complaints often fall into ASP.NET issues rather than Sitefinity itself. But Sitefinity is a large application with over 120 binary files in the bin directory and 100's of configuration settings that need to be loaded when you start the application.
So what are some things that we can do to improve our experiences?
For an improved development experience the easiest one is to recommend better and faster hardware. Make sure you are running on a SSD drive, you have some fast memory modules, (DDR3/DDR4) and a top line multi-core CPU, (Intel i7 processor). Ensure that your CPU and Memory usage are not being excessively consumed after you start your project and if so look at what is consuming those resources. In one case, I came across a machine that was running 3 versions of SQL. Removing the obsolete versions made a difference.
Some Anti-Virus software can slow down the process of developing your applications. When you change a file, add or remove one, your Anti-Virus will scan it to check if it is a virus. As a developer you can imagine how often this happens in your development process. You can remove Anti-Virus scanning on your local source control folder and the ASP.NET Temporary files folder. 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files' You can also remove it from the DevEnv.exe process.
Not every Anti-Virus program will have a big impact on your development. Look to test what impact the change has before committing to it. You can get clearer results by testing the biggest and slowest project you have. A 10% improvement is seen easier on a 10 minutes process than it is on 10 seconds one.
Only add Anti-Virus exceptions if you are confident about what it means to the safety of your machine. If in doubt, don't do it.
Sitefinity supports the 4.5 framework. If you are comfortable, upgrade the application to 4.6, (or 4.6.2). You do this on the project properties and two locations in the web.config file.
The <httpRuntime> and <compilation> attribute targetFramework="4.6".
I have been running this setup for a while now but be aware. Sitefinity do not officially support it and ensure that the platforms that you are deploying to have the framework installed.
Sitefinity 10 does support 4.6 officially.
The 4.6 release had some improvements in ASP.NET with the biggest being in the Task class. But you will get the most improvements when running in conjugation with IIS 8.0, 8.5 or 10. (or IIS Express 10)
Running the site in IIS as opposed to IIS Express is considered faster by some but the truth is the two programs are the same. The main difference with IIS is that it is running as a service and so it is running in advance as opposed to IIS Express which will need to fire up the first time you run your project. Another primary difference is that the application host config is not global with Express. With release 10 and Visual Studio 2015 you will find that the application host file is stored in the .vs directory in the project. You can edit this file to provide particular requirements such as binding to specific ports and SSL. Finally, don't use Visual Studio's Cassini or the hosting functionality that comes with the Sitefinity project manager. The first is obsolete and the second is designed for trials and reviews.
I suppose it goes without saying that the later versions of the Microsoft operating systems and SQL databases will also be more performative than there predecessors. As of today, I have a Surface Pro 3 with Windows 10, Visual Studio 2015, SQL Express 2016 and IIS Express 10. All with the latest patches and service packs applied.
In my future, I am eyeing up Visual Studio 2017 and a Microsoft Surface Pro 5.
Disable Inline Editing
Often when we are developing we are looking at our site as an admin account. If you have inline editing enabled then the resources required for this need to load. Go to Settings -> Advanced -> Pages -> Enable in-line editing and set it to false.
Often when a global.ascx file is added to our project the default methods are also left empty even though we are not using them. When our application runs, IIS checks if these methods exist and if they do, it adds them to processing pipe line which only results in another extra process in our processing. Another issue is that an empty method can throw an application error when dealing with CDN resources, (See a previous post). So make sure you remove or, if possible combine functions to reduce these extra methods loading.
Also consider that if you are using the Request Start and End methods that these methods fire for every request, including requests for images and static files. If you can move this code to a better place then that can save a lot of pointless processing. The same for the Session start and end methods. Does this code need to run on every request? Can this code be moved else where?
Remove Unused Config modules
Sitefinity already does a good job of providing a list of modules that can be removed. But, if your site was created with an earlier version of Sitefinity things may have changed. Check your current web.config modules, and other settings against a web.config generated by a new installation of Sitefinity to ensure you don't now have obsolete modules being loaded.
Remove unused Sitefinity Modules
If you are not using the Sharepoint or Marketo intergrator's then uninstall them from the site. Look at other modules you don't use and can remove. This will help improve your start up times.
Filter Queries by View Permission
When Sitefinity was first released, by default this setting was true. This allowed, as an example that when you displayed a document list, if the user did not have permission to the document then it was not included. Sounds cool. But the the downside is that this caused a lot of performance problems. The need to check all permissions was very heavy and so this value was turned off by default in Sitefinity 5. If you have an older release check that this permission is disabled, (if you are not using it). Go to Administration -> Settings -> Advanced -> Security -> Enable filtering queries by view permissions and disable it.
One thing that has changed from old releases is an entry the appSettings list.
This tells ASP.NET to use an entirely new asynchronous pipeline which follows CLR conventions for kicking off asynchronous operations, including returning threads to the ThreadPool when necessary. ASP.NET 4.0 and below followed its own conventions which went against CLR guidelines, and if the switch is not enabled it is very easy for asynchronous methods to run synchronously, deadlock the request, or otherwise not behave as expected.
Now that we are running our site on at least 4.5 we should ensure that this appSetting is present.
BrowserLink is False
In our web.config file you will find the attribute runAllManagedModulesForAllRequests on the
This attribute is a bit trickier than you may think. Look at this post by blogger Rick Strahl at West Wind to get a better understanding of what is going on here.
Development, Compilation Batch=false
If True, eliminates the delay caused by the compilation required when you access a file for the first time. When this attribute is set to True, ASP.NET pre-compiles all the un-compiled files in a batch mode, which causes an even longer delay the first time the files are compiled. However, after this initial delay, the compilation delay is eliminated on subsequent access of the file. The default is True.
In most cases we are only testing a page or two so don't need the whole application compiled for testing.
Set this setting to false in development but true for production. It will have more impact on Web Form based sites.
Compliation - optimizeCompilations=true
Specifies whether dynamic compilation will recompile an entire site if a top-level file is changed. Top-level files include the Global.asax file and all files in the Bin and App_Code folders. If True, only changed files are recompiled. The default is False.
In development you can set this value to false but change it to true in production.
I have found that this caused me issues with embedded views and templates. I needed to delete the ASP.NET Temporary files to get my changes seen. It may been my issue but do read this MSDN post on the topic first.
And Just For Completeness
Make sure debug="true" is only present in development. Never have it set to true in production. Either make sure the attribute is deleted or marked as false. Also, make sure that your production build is a release build and not a debug build.
Don't build excess projects
If your in the middle of development you probably do not need to be compiling excess projects that have little to do with what you are working on. As an example, I sometimes have side projects used for particular testing or even a database project. These only need to be built\compiled when I am actively working on them. Use the Configuration Manager and create different build profiles to help speed up your build time by avoiding projects that are not currently being required.
Some people will faint when I say this, but I have one configuration profile where I don't build any of my test projects for when I am experimenting with code and configuration.
Remove Extra View Engine Look-ups
By default the Web Form and Razor Views Engines are loaded and 28 locations are searched. You can reduce this lookup by removing the Web Forms View Engine and the .vbhtml file look ups. Check out a my previous post on Removing Excess View Location Look ups.
If you are using a class library for your Feather widgets, (rather than inside the main SitefinityWebApp project) you can also embed your Views there too. But this means recompiling and restarting the application every time you make an HTML syntax change. To help speed up the process, place your Views in your ResourcePackages folder first. If you make a mistake you can edit and save the file while the site is running and just refresh the page to see the results. Later, you can look to move the finalised file to be embedded if you prefer to keep them altogether.
By default Sitefinity has this attribute on the
By setting this to true we get the following. When a page is first requested and it is being compiled and put into the output cache, any further requests will wait for the cached version of the page rather than starting a second request doing the same thing as the first request is currently doing.
So why is it set to false to start with? It sounds the better way to go? Setting it to true can cause errors when the response is modified on its return. The ASP.NET Web Forms Cache Substitution causes such an error. The Sitefinity (Web Forms) Login Status widget uses cache substitution and will throw an error if this attribute is set to true. If you are not using this widget and not using Cache Substitution in your code then set this value to true for better performance.
Any errors thrown in an application are resource heavy, blocking events. They cause performance issues. I have seen many a sites log filled with regular errors but they are ignored because they don't affect the end user or are considered normal. They are not normal! There will always be some errors thrown but you should look to ensure there is nothing you can do to prevent it in your application. If there are, investigate and fix them. If it is not your code but Sitefinity's, then log a fault.
In your code you may be tempted to just use try, catch blocks to handle them. This is better than nothing but you should also look to write (defensive) code that will not throw errors in the first place. But, I have to admit I have had to resort to them at times just because the issue is beyond my control.
A common practice for developers is to use the Response.Redirect method to push requests to other pages and there is nothing wrong with that. But the most common approach is to use Response.Redirect(url). The issue here is that this causes the page life cycle to abort its thread and this causes an application error. It's all hidden and handled by ASP.NET but there is a better, safer, cleaner and more performant option.
Page.Response.Redirect(url, false); HttpContext.Current.ApplicationInstance.CompleteRequest(); Page.Visible = false;
With this code we are allowing the page life cycle and the thread to complete and end in a safe manner, without throwing a hidden application error.
In each cache profile there are three Boolean options. Two of them, Vary By User Agent and Vary by Host both determine if a different version of the page should be cached for different browser versions and host headers. In the old days we sometimes needed to write different HTML, CSS, JS for different browsers but today things are more standardised. Also, the user agent string is often changed depending on the operating system it is on so you can almost get a one user, one cached page scenario. Not very efficient. I always deselect these two options.
With Sitefinity 10 there is no a whole new documentation on cache profiles which you should now read.
The third is Wait for page Output cache to fill. What happens here is that if your page is accessed by 10 requests at the same time and there is no cached version of the page then the first request will process the page and put it in the cache while the other nine requests wait until they can access it from the cache. The alternative is that all 10 requests run and process the page causing more resource overhead. I recommend you tick this box.
Wasn't this same thing talked about earlier in this post? Yes. The main difference is the first is ASP.NET while the second is Sitefinity. I don't know at this stage the relationship, if any but am trying to find out.
Reindex Your Database
Your database can become fragmented over time and this will slow overall performance. Look to rebuild the indexes on regular basis.
Performance Tips From Sitefinity
You should ensure you have read the suggestions from Sitefinity.
Finally, with Sitefinity 10 .svg images are now supported. SVG files are effectively a mark up language and look just like HTML files. The advantage is that they scale unlike traditional images. Really good for icon files. You have the one .svg image and no matter if the screen is a small mobile device or a 52" TV, the same file is used and scales to the size required with no distortion.
Sitefinity 10 provides a new site warmup module. It effectively looks at all your pages and goes through rendering and placing them in the output cache. No need for that first user request. By default it is off and so you need to go into the Sitefinity modules and enable it.
Before Sitefinity 10 you could have a custom process that you ran to request every page or even used a ASP.NET web config setting that also did the same thing on start. Look up applicationInitialization for more information. (You do need to be running on IIS 8 or greater to use this.)
Why Doesn't Sitefinity do all this Stuff by Default?
It would be great if the Sitefinity team just implemented all these improvements but the issue for them will always come down to backwards compatibility. The last thing they want is for people to update their site with the latest release and it not work because people have coded against a previous implementation. This will only generate a lot of support tickets and grumpy posts.
I have seen this often with older Sitefinity projects implementing there own versions of missing features and functionality that Sitefinity did not have at the time. I always encourage to re-code and take advantage of the new Sitefinity features and recommendations but this is not always possible.
What I would like to see more of from the Sitefinity team is a table of features and functionality that is due to be decommissioned. Currently they are looking at removing the Silverlight features, (video player and Analytics). I say great to these moves, (if justified). The less old technology that Sitefinity has to support and test the faster they can then push out improvements. I would like to see a page maintained with such future plans and an explicit area in the release notes advising people of such changes and what code they may need to consider changing before they upgrade.
I would suggest you also look to keep your site up to date and remove older technology as a matter of course. I always encourage owners to have a budget for maintenance which includes upgrades for both Sitefinity releases and code.
Thanks for reading and feel free to comment - Darrin Robertson