Performance tips

ASP.NET Core is one of the fastest platforms on the fundamentals of web request routing, as per benchmarks. Read the complete story at https://www.techempower.com/blog/2016/11/16/framework-benchmarks-round-13/. In this section, we will discuss the points we can use to achieve better performance in our ASP.NET Core apps:

  • It might sound repetitive, but please use async all the way. This is key to the performance of an ASP.NET Core app.
  • Test, test, and test! Perform load testing of your app early and often to find the issues early in development. Our team, in fact, came up with a radical idea to identify performance bottlenecks. The idea is to write middleware, that calculates the response time of the API, and if the response time is higher than the threshold, throw an exception, so the developer has to fix it. I would not recommend going this drastic but the intent is to identify the performance issues early in the game. If your app is deployed in Azure, you can do the performance testing in the Azure portal itself, using the following simple steps (here I am assuming there is no authentication header needed, otherwise we can write Visual Studio performance tests as well):
    1. In the Azure portal, navigate to the App Service that we wish to performance test.
    2. On the left panel of the App Service, there is an item called Performance test. Click on it. It will open the Performance test blade.
    3. Enter the required fields for user load and duration. Each of the fields has a help tool tip so it should be easy to identify the purpose of each field.
    4. Configure the test to use either a manual URL or a Visual Studio web test. If you select manual test, you will need to specify the URL that needs to be load tested. If of a Visual Studio web test, you will need to upload the Visual Studio web test file.
    5. Click Done and then click Run test.
    6. The performance test will run and display the run stats, such as successful requests, failed requests with errors, memory and CPU usage, and so on:
  • Use caching to store static, less frequently changing and frequently accessed data. If you are building an enterprise application, consider leveraging the Azure Redis cache for fast data access.
  • Use the garbage collector in server garbage collection mode. This will ensure that your memory footprint doesn't increase over time. This can be done by the following configuration in the web.config file, or add an app.config file with the following code:
<configuration> 
<runtime>
<gcServer enabled="true"/>
</runtime>
</configuration>
  • If you make use of web APIs in your application, use HttpClient to make the API calls. Remember to create the HttpClient instance only once and reuse it multiple times, that is, create a singleton instance of HttpClient and don't create it every time. In terms of Dependency Injection, use it as singleton and not as transient or scoped.
  • While developing the API, follow these rules:
    • Bring only the data that you need: For the purpose of code re-usability, I have seen teams using a single API that returns a plethora of data, even though only a part of it is needed. For a small number of users, it may not perform badly, but as the user load increases, this will start causing performance bottlenecks, so be very miserly with the data that your service returns.
    • Choice of serializer: A considerable amount of time is spent by a service API in serializing the data and then sending it as a response. The client then gets the response and deserializes it back which again takes time. It is worth investing in a serializer that does the job faster, to provide better response time performance. There are a number of serializers at the developer's disposal such as JSON, BSON, MessagePack, Protocol Buffers, and so on. We recently changed JSON to MessagePack and found massive performance gains, as a MessagePack payload is about 66% of JSON and about three times as fast.
    • Compression: Think of compressing the data sent from the service API to the client. This will be beneficial to mobile users, as well as to whoever may be using your app on a flaky and low bandwidth network, so less data to load would make apps faster. Also, by compressing the payload, we make our application scalable as the bandwidth available to us is limited. There are numerous ways of doing it. Of course, HttpClient has support for GZip compression so we can leverage it. Equally important is the fact that we can choose what properties to serialize. So, if your entity has ten properties and you need only two properties, then it makes perfect sense to serialize only those two properties and ignore the remaining eight properties, to reduce the payload. This is well supported in JSON and we made extensive use of it, and in a few cases came down from 32 MB data to less than 1 MB data. Imagine if this API is called by hundreds of users!
  • Make good use of bundling and minification, as discussed in an earlier chapter.
  • Response caching reduces the number of requests a client or proxy makes to a web server. Response caching also reduces the amount of work the web server performs to generate a response, hence improving the performance. Please make a note that response caching is not supported for ASP.NET Core Razor pages, but support is expected to come in ASP.NET Core 2.1.
  • While using parallelism or multiple threads to write data, make use of concurrent collections. In fact, if you think you may have multiple threads modifying a collection, it's always safe to use concurrent collections, with little overhead.
  • Avoid API or database calls inside a loop. In the case of APIs, try to create an API that takes the collection as input and processes that to return a consolidated but trimmed set of required data. In the case of a database, create a stored procedure that accepts a user-defined table as a parameter and returns the data in one go. This will make the application less chatty.
  • Do not perform string concatenation inside loops. Use StringBuilder if you need to concatenate strings inside loops.
  • Visual Studio has great support for profiling your application to identify high CPU issues, so do make good use of the Visual Studio profiler while in the development phase. It's very simple to use, as illustrated in these steps:
    1. Ensure that the app that you wish to profile is up and running in the machine.
    2. Open Visual Studio and in the quick launch, search for Performance Explorer. Alternatively, you can navigate through Debug | Profiler | Performance Explorer | Attach/Detach, as shown here:
    1. It will open a dialog, displaying the list of running processes. Select the process that you wish to profile and click Attach.
    2. The profiling will start. Now is the time to reproduce your high CPU issue and once it is reproduced, stop the profiling. It will generate a detailed profiling report. We will see what the profile report looks like a little later when we discuss profiling in Azure.
  • Profile your memory to identify whether there is a memory leak. Always remember, that if your memory percentage remains constant or keeps increasing over a period of time, there may be a memory leak. Memory leak can be described as a situation where a program holds on to the memory even if that memory is discarded and no longer needed. Due to bad coding, the developer code can prevent the Garbage Collector (GC) from reclaiming the memory, and hence, used memory keeps increasing over a period of time, resulting in performance issues or failures. Apart from Visual Studio, there are many good profiling tools that can be used such as dotTrace, MemProfiler, ANTS Memory Profiler, PerfView, to name a few. One of the common causes of memory leak in the ASP.NET Core applications that I have encountered is wrong Dependency Injection. So, if an object needs to be scoped or transient and you register it as a singleton, we may be injecting memory leak if it's not meant to be a singleton. Static objects and dictionaries are another common cause of memory leak, so please think multiple times before marking an object as static.
  • ASP.NET Core has support for analyzers. Code analysis, as the name suggests, is the analysis of the code to identify potential code issues, such as improper coding, noncompliance to standards, security violations, and design problems. Code analysis can be static or dynamic. In static, the analysis is done without actually running the code. StyleCop, FxCop are few of the most well-known and frequently used code analyzers. Make use of analyzers to identify code issues early. A few of the great ones are Microsoft.CodeAnalysis, SonarAnalyzer.Csharp, FxCop analyzer, Roslynator.Analyzers, and Stylecop analyzer, to name a few. These are also simple to use—right-click on the project and select Nuget Package Manager. Search for the analyzer of your choice and install it. Build your project and observe the warnings and errors in the error window. This will help you nail a variety of issues, such as possible performance bottlenecks, security vulnerabilities, as well as not following best practices.
Read this excellent MSDN blog post on performance improvements in ASP.NET Core: https://blogs.msdn.microsoft.com/dotnet/2017/06/07/performance-improvements-in-net-core/.
Also, read the official performance documentation of ASP.NET Core at https://docs.microsoft.com/en-us/aspnet/core/performance/.

Next, we will look at a few of the cool and handy features of Azure that can help us be more productive.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset