C#
.NET
programming
optimization
software development

Tips for optimizing C/.NET programs

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Optimizing C# and .NET code starts with one rule: measure before you change anything. The runtime, JIT, garbage collector, and libraries are already highly optimized, so performance work is most effective when it targets a proven bottleneck instead of general intuition.

Start With Measurement

Before rewriting code, use a profiler or at least a reproducible benchmark. Without that, it is easy to make code harder to read for no meaningful gain.

For real applications, measure:

  • CPU hotspots
  • allocation rate
  • GC pressure
  • database or network latency
  • lock contention

A slow request is often not slow because of arithmetic. It is slow because of I/O, allocation churn, or excessive synchronization.

Reduce Unnecessary Allocations

Allocation-heavy code puts more pressure on the garbage collector. One common example is repeated string concatenation in loops.

csharp
1using System.Text;
2
3var builder = new StringBuilder();
4for (int i = 0; i < 1000; i++)
5{
6    builder.Append(i).Append(',');
7}
8string result = builder.ToString();

For small one-off cases, normal string interpolation is fine. For repeated accumulation, StringBuilder is usually the better choice.

Choose Data Structures Carefully

Algorithm and data-structure choices often matter more than micro-optimizations.

Examples:

  • use Dictionary<TKey, TValue> for key lookups instead of repeated linear scans
  • use HashSet<T> when membership testing matters
  • use arrays when size is fixed and indexed access is the main operation

If a hot path does repeated List<T>.Contains, replacing it with a HashSet<T> can outperform many low-level tweaks combined.

Be Careful With LINQ on Hot Paths

LINQ is expressive and often perfectly fine, but in tight loops or large pipelines it can create extra allocations or repeated enumeration if used carelessly.

Readable code:

csharp
var adults = people.Where(p => p.Age >= 18).ToList();

Hot-path alternative when profiling proves it matters:

csharp
1var adults = new List<Person>();
2foreach (var person in people)
3{
4    if (person.Age >= 18)
5    {
6        adults.Add(person);
7    }
8}

Do not replace all LINQ preemptively. Replace it only when the measured cost matters.

Avoid Blocking and Over-Synchronization

Performance issues in .NET programs are often about waiting, not computing.

Examples include:

  • blocking on async work with .Result or .Wait()
  • serializing too much work through one lock
  • holding locks during I/O

If the code is I/O-bound, async patterns can improve throughput far more than CPU-level tweaks.

Cache Only When It Helps

Caching can be powerful, but it is not free. It increases memory use and invalidation complexity.

Cache results when:

  • the data is expensive to compute or fetch
  • reuse is high
  • staleness rules are clear

Do not add caching just because a method looks "important." Measure the benefit.

Common Pitfalls

The most common mistake is optimizing without profiling, which often targets the wrong part of the program.

Another mistake is focusing on tiny language-level tweaks while ignoring larger wins such as algorithm choice, database access patterns, or allocation churn.

A third pitfall is making code unreadable for a theoretical speedup that never shows up in production measurements.

A fourth pitfall is skipping after-change measurement and assuming an optimization helped just because it looks lower level.

Summary

  • Measure before optimizing.
  • Reduce allocations and choose data structures that match the workload.
  • Treat LINQ, strings, and collections as optimization targets only when profiling identifies them.
  • Many real .NET performance problems are about I/O, blocking, or synchronization rather than raw CPU work.
  • Prefer changes that improve both performance and maintainability when possible.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.