.NET 5 minute read

ASP.NET Core Performance: Measure Before You Optimize

A practical way to investigate slow .NET APIs using traces, database timings and allocation data before changing code.

A slow API endpoint does not automatically mean the C# code needs to be faster. In enterprise systems, the largest delays are often outside the method you’re looking at: database calls, external services, network latency or contention.

Before rewriting a query or adding caching, collect evidence about where the request spends its time.

1. Start with the request path

Capture timings across the full path: reverse proxy, authentication, controller or minimal API endpoint, application services, database and external calls. Distributed tracing can make a slow dependency visible without guessing which layer is responsible.

Useful questions to ask:

  • Is the latency consistently high, or only at the 95th and 99th percentiles?
  • Are database round trips increasing with the number of records?
  • Is a downstream dependency responsible for most of the wait?
  • Does latency rise with concurrency even when CPU usage stays low?

Averages are useful, but they can hide the requests users are actually complaining about.

2. Check data access before micro-optimizing C#

A single missing index, an unexpected query plan or an N+1 access pattern can outweigh many small in-process optimizations. Inspect actual execution plans and query statistics. Confirm that the application is fetching only the data it needs.

For example, selecting one hundred rows and making an additional query per row can create many unnecessary network round trips. Sometimes batching or shaping the original query is the biggest win.

3. Treat allocations as evidence, not the target

Allocations matter under load because they increase garbage collection work. But don’t rewrite readable code just to remove a few small allocations without measuring the impact.

Use profiling to inspect allocation hotspots, garbage collection pauses and the correlation with request latency. Prefer changes supported by before-and-after measurements under representative conditions.

4. Make the improvement observable

For every optimization, record the baseline, implementation change and measured result. Compare under similar traffic, dataset and environment conditions.

A good performance fix should improve a relevant outcome — such as tail latency, throughput, resource cost or stability — without sacrificing maintainability.

The principle: first understand where time is spent. Optimize the bottleneck, not the code that looks suspicious.

Have a similar challenge?

I work with teams on .NET architecture, Azure cloud and applied AI integration.

Let's talk ↗