Skip to main content

Performance Optimization Basics

Level: Intermediate to Advanced

ℹ️ What You'll Learn
  • Measuring performance
  • Profiling .NET applications
  • Identifying bottlenecks
  • Memory optimization
  • Common performance mistakes
  • BenchmarkDotNet basics

Measure Before Optimizing

Always profile before optimizing.

Blindly optimizing wastes time.

Use data to identify slow parts.

Visual Studio Profiler

  1. Debug → Performance Profiler
  2. Select CPU Usage
  3. Run application under load
  4. Analyze results

Shows which functions take most time.

Optimize the hot spots (slow functions).

Common Bottlenecks

1. Database Queries

Most common bottleneck.

Bad:

var students = _db.Students.ToList();
foreach (var s in students)
{
var marks = _db.ExamResults.Where(r => r.StudentId == s.Id).ToList();
// N+1 problem: 1 query for students + 100 queries for marks
}

Good:

var students = _db.Students
.Include(s => s.ExamResults)
.ToList();
// 1 query with JOIN

2. Unnecessary Object Allocation

// Bad: Creates string each iteration
for (int i = 0; i < 1000; i++)
{
var s = "Student" + i.ToString();
}

// Good: Use StringBuilder
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
sb.Append("Student").Append(i);
}

3. Synchronous I/O

// Bad: Blocks thread
var data = _db.Students.ToList();

// Good: Async, frees thread
var data = await _db.Students.ToListAsync();

Async is essential for web applications.

Benchmarking with BenchmarkDotNet

Compare performance of different approaches.

Install:

dotnet add package BenchmarkDotNet

Write benchmark:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

[MemoryDiagnoser]
public class StringBenchmark
{
[Benchmark]
public string StringConcat()
{
string result = "";
for (int i = 0; i < 1000; i++)
result += i;
return result;
}

[Benchmark]
public string StringBuilder()
{
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
sb.Append(i);
return sb.ToString();
}
}

static void Main()
{
var summary = BenchmarkRunner.Run<StringBenchmark>();
}

Run:

dotnet run -c Release

Results show which is faster.

Memory Profiler

Visual Studio → Debug → Memory Usage

While running application:

  • Inspect heap size
  • Find memory leaks
  • Identify large objects

Look for growing memory over time (leak indicator).

Optimization Techniques

Use ValueType for Small Objects

// Bad: Heap allocation
public class StudentId { public int Value; }

// Good: Stack allocation
public struct StudentId { public int Value; }

Struct uses stack, faster (but immutable).

Cache Expensive Operations

// Bad: Recompute every time
public int GetStudentCount()
{
return _db.Students.Count(); // SQL query every call
}

// Good: Cache result
private int _studentCount = 0;
private DateTime _cacheTime = DateTime.MinValue;

public int GetStudentCount()
{
if ((DateTime.Now - _cacheTime).TotalMinutes > 1)
{
_studentCount = _db.Students.Count();
_cacheTime = DateTime.Now;
}
return _studentCount;
}

Use LINQ Efficiently

// Bad: Loads all into memory then filters
var students = _db.Students.ToList().Where(s => s.Name.StartsWith("A"));

// Good: Filter in database
var students = _db.Students.Where(s => s.Name.StartsWith("A")).ToList();

ToList() must be last (after filtering/selecting).

Profiling Tools

ToolPurpose
Visual Studio ProfilerBuilt-in, full featured
dotTraceJetBrains, advanced profiling
New RelicProduction monitoring
DataDogApplication performance monitoring

Performance Best Practices

  1. Profile before optimizing — Measure actual bottlenecks
  2. Optimize database queries first — Usually the slowest
  3. Use async/await — Don't block threads
  4. Cache expensive results — In-memory caching
  5. Batch operations — Fewer database round trips
  6. Monitor production — Real-world performance data
  7. Benchmark changes — Verify improvements
🎯 Interview Favourite

Q: How do you identify and optimize performance bottlenecks in a .NET application?

Good Answer: "First, I use the Visual Studio Performance Profiler under real workload to identify where CPU time is spent. Usually the biggest bottleneck is database queries - I look for N+1 problems (querying inside loops) and fix them with Include() or batch loading. I benchmark string concatenation vs StringBuilder for high-volume operations. For web APIs, I ensure operations are async to free threads for other requests. I use caching for expensive operations that don't change frequently. Before optimizing anything, I measure to avoid premature optimization. After changes, I re-profile to verify the improvement actually happened."

🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Performance optimization in .NET. Try these prompts:

  • "How do I find performance bottlenecks in my .NET app?"
  • "What is the N+1 problem and how do I fix it?"
  • "When should I use async/await?"
  • "How do I use BenchmarkDotNet to compare approaches?"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

Section Complete

You have completed the .NET Platform Fundamentals section.

Next recommended step: Start C# Fundamentals

nexcoding.in