Improving Asp.Net MVC Page Rendering
Posted on: October 20, 2015
Asp.Net MVC view engine is not very efficient. Usually, the database is the bottle neck, but with Asp.Net, the view engine can be the one to slow down your system. This is especially true if you are using a lot of partial views, templates and service code. This is a little bit ironic because good patterns suggest to divide. Nevertheless, we can improve it by trying to reduce some of the tax that the system is producing.
First of all, building in release instead of debug will help, but this will not be a game changer. This is something that stun me for several reasons. Mostly because if you search on Internet, this is the big optimization to do, but also because the compilation in release does not optimize the rendering properly. Here are three screenshot taken of Glimpse. All benchmark results are from the same page that we will optimize during this article. The first one is the webpage in debug, without caching of any database calls.
The second one benchmark has the web application not reaching the database at all -- it use Redis to cache all the data queries.
Redis cache is powerful and give us an advantage of 10% in the scenario under test. The performance gains can be even more with a database with more values. This is why, it is worth it. However, you can still see that it takes 3 seconds to load the page -- this is not acceptable.
The next image is with the cache enabled and compiled in release mode (also with the web.config is set to to the compilation to debug at false). The gain in performance is interesting with a result at 2.27 seconds, 25% faster. The result is appreciable but still does not make any sense. This is unfortunate because if you search on Internet, this is about what they suggest to help the performance of your application. You can cache the generated output, but this is good only if it is acceptable to not have fresh values. This is not really an option in many scenario. I never been a big fan of caching the output because it's harder to invalidate. Unlike caching database results or calculus results with Redis on the back-end which can be invalidate and re-set by the back-end, the front end require to do an http calls to refresh the output cache. In any case, the problem is the rendering of the view.
With the help of Glimpse, I were able to see that the Razor view engine was producing 108 views, all cached. Instead of having a system well separated (on the views), the next step of optimization is to use directly in the .cshtml the creation of the Html. It's also interesting to see the when we build in debug that the amount of call to the view engines double.
After removing most of the Display Templates, the performance increased. The time dropped from 2.27 seconds to 551ms (debug)/ 509ms (release). In the remaining time, most of it is still in the construction of Html by the Razor Engine. Instead of having 108 views, only 8 was used. This is clearly the consequence of reducing the use of display template. Unfortunately, the performance gain come with the cost of having a code that is less reusable. This new benchmark got the html of the template copy-pasted in some area of the .cshtml. The next step is to transfer this reusable code inside Html Helper. Also, even if we have better performance, I am still far from what I can do with PHP. This is something that is still bothering me. I am benchmarking an application that is a written of a PHP application. The backend performance (logic + database) is about the same, but the rendering is a lot slower. On the 509ms, 358ms is on the creation of the view. In the PHP application, less than 50ms was in the creation of the html -- still 6 times slower.
Build in Debug with reduction of Display Template:
Build in Releasewith reduction of Display Template: