C# Under the Hood: How to Speed Up Code with Profiling

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Is Your C# Application Slow? Time to Look Under the Hood



You've written beautiful, clean C# code, but it runs slower than you'd like. Sound familiar? In modern development, C# performance often becomes a decisive factor: users won't tolerate long load times, and server applications must handle thousands of requests per second. The good news: .NET provides powerful tools for .NET diagnostics and C# profiling. The bad news: without understanding the process, you risk optimizing the wrong things.



In this article, we'll break down how to properly profile C# code, find real C# bottlenecks, and apply proven C# code optimization techniques. You'll learn why intuitive guesses often fail, and how to use BenchmarkDotNet and ETW tracing to get objective data. Let's go!



Why "By Eye" Doesn't Work: Optimization Myths



Before diving into C# code optimization, let's debunk popular misconceptions. Many developers think they know where their program is slow. In practice, C# profiling shows: 90% of time is spent on 10% of the code, and these "hot spots" are often in unexpected places.



Typical mistakes:

  • Premature optimization — you spend hours speeding up sections that run once a day.
  • Trusting intuition — "This LINQ query is definitely slow" (when the real problem is serialization).
  • Ignoring GC — the garbage collector can "eat" up to 30% of CPU time if you manage memory incorrectly.


The conclusion: C# performance needs to be measured, not guessed. Professional tools exist for this purpose.



Toolkit for C# Profiling



The .NET ecosystem offers several ways to perform .NET diagnostics. Let's look at the main ones.



BenchmarkDotNet — Your Best Friend



BenchmarkDotNet is the gold standard for microbenchmarking. It automatically runs code multiple times, warms up the JIT, accounts for noise, and produces statistically significant results. Use it to compare two implementations of the same method.



Example: compare a regular for loop and foreach for an array:



using BenchmarkDotNet.Attributes;using BenchmarkDotNet.Running;

[MemoryDiagnoser] // measure memory allocationpublic class LoopBenchmarks{ private int[] _data;

[GlobalSetup] public void Setup() { _data = Enumerable.Range(0, 1000).ToArray(); }

[Benchmark] public int ForLoop() { int sum = 0; for (int i = 0; i < _data.Length; i++) sum += _data[i]; return sum; }

[Benchmark] public int ForeachLoop() { int sum = 0; foreach (var item in _data) sum += item; return sum; }}

class Program{ static void Main() => BenchmarkRunner.Run<LoopBenchmarks>();}


Running this code, you'll see that C# performance for foreach on arrays is identical to for — the JIT optimizes both variants equally. But for List<T>, foreach may be slower due to bounds checks.



Visual Studio Profiler and dotMemory



For full-scale C# profiling at the application level, use the built-in Visual Studio profiler (available in Enterprise edition) or the free dotMemory from JetBrains. They will show:

  • Which methods consume the most CPU.
  • How much memory is allocated and where.

Blogs

Book Recommendations