ASP.NET Core Performance Best Practices

This article provides pointers for performance best practices with ASP.NET Core.

Cache aggressively

Caching is mentioned in many components of this document. For a lot of information, see Response caching in ASP.NET Core.

Understand hot code paths

In this document, a hot code path is outlined as a code path that’s oft referred to as and wherever abundant of the execution time occurs. Hot code ways generally limit app scale-out and performance and are mentioned in many elements of this document.

Avoid blocking calls

ASP.NET Core apps ought to be designed to method several requests simultaneously. Asynchronous arthropod genus permit alittle pool of threads to handle thousands of synchronous requests by not waiting on blocking calls. instead of waiting on a long-running synchronous task to complete, the thread will work on another request.

A common performance downside in ASP.NET Core apps is obstructing calls that would be asynchronous. several synchronous blocking calls result in Thread Pool starvation and degraded response times.

Do not:

  • Block asynchronous execution by business Task.Wait or Task.Result.
  • Acquire locks in common code methods. ASP.NET Core apps are most performant once architected to run code in parallel.
  • Call Task.Run and now wait it. ASP.NET Core already runs app code on traditional Thread Pool threads, therefore calling Task.Run solely ends up in further unessential Thread Pool scheduling. although the regular code would block a thread, Task.Run doesn’t stop that.


  • Make hot code paths asynchronous.
  • Call knowledge access, I/O, and long-running operations arthropod genus asynchronously if an asynchronous API is available. don’t use Task.Run to form a synchronous API asynchronous.
  • Make controller/Razor Page actions asynchronous. The entire decision stack is asynchronous so as to learn from async/await patterns.

A profiler, adore PerfView, may be wont to notice threads oft more to the Thread Pool. The Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start event indicates a thread added to the thread pool.

Return IEnumerable or IAsyncEnumerable

Returning IEnumerable<T> from Associate in Nursing action ends up in synchronous assortment iteration by the serializer. The result’s the obstruction of calls and a possible for thread pool starvation. To avoid synchronous enumeration, use ToListAsync before comeing the enumerable.

Beginning with ASP.NET Core 3.0, IAsyncEnumerable<T> may be used as another to IEnumerable<T> that enumerates asynchronously. For additional information, see Controller action return types.

Minimize large object allocations

The .NET Core garbage collector manages allocation and launch of reminiscence robotically in ASP.NET Core apps. Automatic garbage series usually manner that builders do not want to fear approximately how or while reminiscence is freed. However, cleansing up unreferenced items takes CPU time, so builders ought to reduce allocating items in warm code paths. Garbage series is mainly steeply-priced on big items (< 85Kb). Large items are saved at the big item heap and require a full (technology 2) garbage series to easy up. Unlike technology zero and technology 1 collections, a technology 2 series calls for a brief suspension of app execution. Frequent allocation and de-allocation of big items can motive inconsistent performance.


  • Do remember caching huge gadgets which can be regularly used. Caching huge gadgets prevents high-priced allocations.
  • Do pool buffers via way of means of the use of an ArrayPool to save huge arrays.
  • Do now no longer allocate many, short-lived huge gadgets on warm code paths.

Memory issues, along with the preceding, may be recognized via way of means of reviewing rubbish collection (GC) stats in PerfView and examining:

  • Garbage series pause time.
  • What percent of the processor time is spent in garbage series.
  • How many garbage collections are era 0, 1, and 2.

For more information, see Garbage Collection and Performance.

Optimize data access and I/O

Interactions with a records save and different faraway offerings are regularly the slowest elements of an ASP.NET Core app. Reading and writing records effectively is essential for proper performance.


  • Do call all the data access to get access to APIs asynchronously.
  • Do not retrieve extra information than is vital. Write queries to go back simply the information it really is vital for the contemporary HTTP request.
  • Do keep in mind caching regularly accessed information retrieved from a database or far flung provider if barely out-of-date information is acceptable. Depending at the scenario, use a MemoryCache or a DistributedCache. For extra information, see Response caching in ASP.NET Core.
  • Do limit community spherical trips. The intention is to retrieve the desired information in a single call rather than several calls.
  • Do use no-monitoring queries in Entity Framework Core whilst having access to information for read-simplest purposes. EF Core can go back the outcomes of no-monitoring queries extra efficiently.
  • Do clear out out and mixture LINQ queries (with .Where, .Select, or .Sum statements, for example) in order that the filtering is executed through the database.
  • Do keep in mind that EF Core resolves a few question operators at the client, which might also additionally cause inefficient question execution. For extra information, see Client assessment overall performance issues.
  • Do now no longer use projection queries on collections, that could bring about executing “N + 1” SQL queries. For extra information, see Optimization of correlated subqueries.

See EF High Performance for approaches that may improve performance in high-scale apps:

We suggest measuring the effect of the previous high-overall performance strategies earlier than committing the code base. The extra complexity of compiled queries might not justify the overall performance improvement.

Query problems may be detected through reviewing the time spent gaining access to information with Application Insights or with profiling tools. Most databases additionally make information to be had regarding regularly performed queries.

Pool HTTP connections with HttpClientFactory

Although HttpClient implements the IDisposable interface, it is designed for reuse.Closed HttpClient times go away sockets open withinside the TIME_WAIT country for a quick length of time.If a code route that creates and disposes of HttpClient items is often used, the app might also additionally exhaust to be had sockets. HttpClientFactory become delivered in ASP.NET Core 2.1 as a method to this problem. It handles pooling HTTP connections to optimize overall performance and reliability.


Keep common code paths fast

You need all your code to be fast. Frequently-known as code paths are the maximum crucial to optimize. These include:

Middleware additives withinside the app’s request processing pipeline, specifically middleware run early withinside the pipeline. These additives have a huge effect on overall performance. Code it is done for each request or more than one instances in line with request. For example, custom logging, authorization handlers, or initialization of brief services.


  • Do now no longer use custom middleware additives with long-walking tasks.
  • Do use overall performance profiling tools, including Visual Studio Diagnostic Tools or PerfView), to become aware of warm code paths.

Complete long-running Tasks outside of HTTP requests

Most requests to an ASP.NET Core app may be dealt with with the aid of using a controller or web page version calling essential offerings and returning an HTTP reaction. For a few requests that contain long-jogging responsibilities, it is higher to make the whole request-reaction technique asynchronous.


  • Do now no longer look ahead to long-jogging responsibilities to finish as a part of normal HTTP request processing.
  • Do bear in mind dealing with long-jogging requests with heritage offerings or out of technique with an Azure Function. Completing paintings out-of-technique is mainly useful for CPU-extensive responsibilities.
  • Do use real-time communique options, which includes SignalR, to talk with customers asynchronously.

Minify client assets

ASP.NET Core apps with complicated front-ends regularly serve many JavaScript, CSS, or photograph documents. Performance of preliminary load requests may be advanced via way of means of:

  • Bundling, which mixes a couple of documents into one.
  • Minifying, which reduces the scale of documents via way of means of putting off whitespace and comments.


  • Do use ASP.NET Core’s integrated help for bundling and minifying patron assets.
  • Do recall different third-party tools, including Webpack, for complicated patron asset management.

Compress responses

Reducing the scale of the reaction commonly will increase the responsiveness of an app, frequently dramatically. One manner to lessen payload sizes is to compress an app’s responses. For greater information, see Response compression.

Use the latest ASP.NET Core release

Each new launch of ASP.NET Core consists of overall performance enhancements. Optimizations in .NET Core and ASP.NET Core imply that more recent variations commonly outperform older variations. For example, .NET Core 2.1 brought assist for compiled ordinary expressions and benefitted from Span<T>. ASP.NET Core 2.2 brought assist for HTTP/2. ASP.NET Core 3.0 provides many enhancements that lessen reminiscence utilization and enhance throughput. If overall performance is a priority, don’t forget upgrading to the present day model of ASP.NET Core.

Performance and reliability

The following sections provide performance tips and known reliability problems and solutions.

Avoid synchronous read or write on HttpRequest/HttpResponse body

All I/O in ASP.NET Core is asynchronous. Servers enforce the Stream interface, which has each synchronous and asynchronous overloads. The asynchronous ones ought to be desired to keep away from blocking off thread pool threads. Blocking threads can result in thread pool starvation.

Do now no longer do this: The following instance makes use of the ReadToEnd. It blocks the present day thread to watch for the result. This is an instance of sync over async.

public class BadStreamReaderController : Controller
    public ActionResult<ContosoData> Get()
        var json = new StreamReader(Request.Body).ReadToEnd();

        return JsonSerializer.Deserialize<ContosoData>(json);

In the previous code, Get synchronously reads the complete HTTP request frame into memory. If the patron is slowly uploading, the app is doing sync over async. The app does sync over async due to the fact Kestrel does NOT help synchronous reads.

Do this: The following instance makes use of ReadToEndAsync and does now no longer block the thread even as reading.

public class GoodStreamReaderController : Controller
    public async Task<ActionResult<ContosoData>> Get()
        var json = await new StreamReader(Request.Body).ReadToEndAsync();

        return JsonSerializer.Deserialize<ContosoData>(json);


The preceding code asynchronously reads the entire HTTP request body into memory.

Do this: The following example is fully asynchronous using a non buffered request body:

public class GoodStreamReaderController : Controller
    public async Task<ActionResult<ContosoData>> Get()
        return await JsonSerializer.DeserializeAsync<ContosoData>(Request.Body);

The preceding code asynchronously de-serializes the request body into a C# object.

Leave a Reply

Your email address will not be published. Required fields are marked *

1 × four =

You May Also Like