C sharp - Performance Engineering in C# (.NET Performance Tuning)

Performance engineering is the process of designing, measuring, and optimizing applications to achieve maximum efficiency.

In C#, performance depends on:

  • Memory management

  • Garbage Collection

  • JIT compilation

  • CPU usage

  • Thread management

  • I/O efficiency

This topic focuses on understanding how .NET works internally and how to optimize correctly.


1. Understanding the CLR (Common Language Runtime)

C# code is:

  1. Compiled into IL (Intermediate Language)

  2. JIT-compiled into native machine code at runtime

Performance depends on:

  • JIT optimizations

  • CPU architecture

  • Runtime environment

The JIT compiler performs:

  • Inlining

  • Dead code elimination

  • Loop optimizations


2. Garbage Collection (GC) Optimization

.NET uses automatic memory management.

Objects are allocated on:

  • Stack (value types, local refs)

  • Heap (reference types)

Heap is divided into generations:

Generation Purpose
Gen 0 Short-lived objects
Gen 1 Intermediate
Gen 2 Long-lived objects
LOH Large Object Heap (85KB+)

Why This Matters

Frequent allocations cause:

  • GC pressure

  • CPU spikes

  • Latency

Optimization Techniques

  • Reuse objects

  • Use ArrayPool<T>

  • Avoid unnecessary allocations

  • Prefer structs for small data types

  • Avoid boxing/unboxing


3. Avoid Boxing and Unboxing

Boxing:
Converting value type → object

int x = 5;
object obj = x;  // boxing

Unboxing:

int y = (int)obj;

This causes:

  • Heap allocation

  • Performance overhead

Avoid by:

  • Using generics

  • Avoid storing value types in object collections


4. Use Span and Memory

Span<T> allows working with memory without allocation.

Example:

Span<int> numbers = stackalloc int[5];

Benefits:

  • No heap allocation

  • High performance

  • Safe memory access

Used in:

  • High-performance systems

  • Parsing libraries


5. String Performance

Strings are immutable.

Every modification creates new object:

string s = "";
s += "Hello";

Better approach:

StringBuilder sb = new StringBuilder();
sb.Append("Hello");

Use StringBuilder for:

  • Loops

  • Large concatenations


6. Async for Scalability

Use async for:

  • I/O-bound operations

  • Web servers

  • Database calls

It:

  • Frees threads

  • Improves throughput

  • Reduces memory usage

Avoid blocking calls:

  • .Wait()

  • .Result


7. LINQ Performance Consideration

LINQ is readable but can cause:

  • Extra allocations

  • Deferred execution confusion

Example inefficient:

var result = list.Where(x => x > 5).ToList();

If only counting:

list.Count(x => x > 5);

Avoid multiple enumerations of same collection.


8. Profiling and Benchmarking

Never optimize blindly.

Use tools like:

  • BenchmarkDotNet

  • dotnet trace

  • Visual Studio Profiler

  • PerfView

Measure:

  • CPU usage

  • Memory allocation

  • GC frequency

  • Execution time


9. Threading and Parallelism

Improper threading causes:

  • Context switching overhead

  • Lock contention

  • Deadlocks

Use:

  • Parallel.For

  • Task Parallel Library (TPL)

  • Concurrent collections

Avoid excessive locking.


10. Data Structure Selection

Choosing wrong structure impacts performance.

Use Case Best Structure
Fast lookup Dictionary
Ordered data SortedSet
FIFO Queue
LIFO Stack
Large numeric arrays Array

Algorithm complexity matters:
O(1) is better than O(n)


11. Reduce Exception Usage

Exceptions are expensive.

Do not use for control flow:

Bad:

try
{
    int x = int.Parse(value);
}
catch
{
}

Better:

int.TryParse(value, out int x);

12. Real-World Example

In ASP.NET:

Poor performance:

  • Blocking DB calls

  • Large object allocations

  • Frequent GC

Optimized:

  • Async DB calls

  • Object pooling

  • Efficient caching

  • Minimal allocations

This can increase request handling capacity by multiple times.


Key Principles

  1. Measure before optimizing

  2. Avoid unnecessary allocations

  3. Understand GC behavior

  4. Use correct data structures

  5. Prefer async for I/O

  6. Minimize locking

  7. Avoid boxing