I used to be that developer who added async
and await
to every method. Database calls, file operations, even simple calculations. I thought I was writing "modern" C# code.
I was wrong.
After benchmarking real applications, I discovered that async isn't the performance boost most developers think it is. In fact, it often makes individual operations slower while adding unnecessary complexity.
(This article was originally published on my blog: bytecrafted.dev.)
The Big Misconception: Scalability != Speed
Here's what I wish someone had told me earlier:
Async improves scalability, not speed. It helps your system handle more concurrent users without blocking threads, but it usually doesn't make individual operations faster.
Think of it this way:
- Synchronous code: Serve 50 people quickly, but only 50 at a time
- Async code: Serve 5000 people simultaneously, but each might wait a bit longer
The Numbers Don't Lie
I ran actual benchmarks on .NET 8. Here's what I found:
Simple method calls: Async was 72x slower than sync (1,435ns vs 19.9ns)
CPU-bound calculations: Async with Task.Run
was 60% slower with 10x more memory allocations
Database queries: Async added 5% overhead even for simple EF Core operations
// This adds 60% overhead and 10x memory allocations
public async Task<decimal> CalculateTotalAsync(List<OrderItem> items)
{
return await Task.Run(() => items.Sum(x => x.Price * x.Quantity));
}
// Just do this - 60% faster
public decimal CalculateTotal(List<OrderItem> items)
{
return items.Sum(x => x.Price * x.Quantity);
}
When Async Actually Helps
Don't get me wrong - async is powerful when used correctly. Use it for:
- Remote HTTP calls (third-party APIs, microservices)
- Cloud storage operations (S3, Azure Blob, large file uploads)
- Message queue operations (Service Bus, RabbitMQ)
- High-concurrency scenarios (1000+ simultaneous users)
My Simple Rule: L.A.T.E.
Before making a method async, ask: Is it L.A.T.E.?
- Latency-bound (waiting on slow network/disk)
- Awaiting remote I/O (APIs, cloud services)
- Thread pool constrained (high concurrent load)
- Expensive to block (user-facing bottlenecks)
If none of these apply, start synchronous.
The Real Problem: Async Contamination
Sometimes libraries force async on you. EF Core's FirstOrDefaultAsync()
, HttpClient.SendAsync()
, and cloud SDKs are async-first or async-only.
Solution: Keep async at your application boundaries (controllers, repositories) but keep your domain logic synchronous. Async should be an infrastructure concern, not a business logic concern.
Key Takeaway
Most enterprise applications don't need async everywhere - they need it strategically. Profile your application, identify real bottlenecks, then apply async to solve actual concurrency problems.
I've seen more bugs from async overuse than from sync bottlenecks. Start simple, optimize when you have proof there's a need.
Read the full deep-dive with complete benchmarks and real-world examples: Why Async Can Be Slower in Real Projects?
What's your experience with async overuse? Have you seen performance regressions from unnecessary async/await? Share your stories in the comments!
Top comments (0)