Optimizing this C algorithm
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Optimizing C# algorithms involves identifying bottlenecks and applying targeted improvements: choosing better data structures, reducing allocations, eliminating redundant computation, and leveraging language-specific features like Span<T> and LINQ. This article walks through common optimization techniques with practical examples.
Profiling Before Optimizing
Always measure before optimizing. Use Stopwatch for quick benchmarks and BenchmarkDotNet for rigorous comparisons:
For production profiling, use BenchmarkDotNet:
Optimization 1: Replace O(n²) with O(n)
The most impactful optimization is reducing algorithmic complexity:
Optimization 2: Choose the Right Data Structure
| Operation | List<T> | HashSet<T> | Dictionary<K,V> | SortedSet<T> |
| Search | O(n) | O(1) | O(1) | O(log n) |
| Insert | O(1)* | O(1) | O(1) | O(log n) |
| Delete | O(n) | O(1) | O(1) | O(log n) |
| Ordered | Yes | No | No | Yes |
Optimization 3: Reduce Allocations
Excessive object allocation increases GC pressure. Use value types, Span<T>, and pooling:
Optimization 4: Cache Computed Values
Optimization 5: Use Array/Span over LINQ for Hot Paths
LINQ is readable but adds overhead from delegates and allocations:
Optimization 6: StringBuilder for String Concatenation
Optimization 7: Parallel Processing
For CPU-bound work on large datasets, use Parallel.For or PLINQ:
Common Pitfalls
- Premature optimization: Optimize only after profiling identifies the actual bottleneck. Most code runs infrequently enough that readability matters more than micro-performance.
- Micro-benchmarking mistakes: JIT compilation, CPU caching, and GC pauses skew naive benchmarks. Use BenchmarkDotNet with warmup iterations for reliable measurements.
- LINQ in hot loops: LINQ is fine for cold paths. In tight loops called millions of times, the delegate invocation and iterator allocation cost adds up. Switch to
forloops in hot paths. - Boxing with generics: Using
objector non-generic interfaces with value types causes boxing. Prefer generic methods and interfaces (IComparable<T>overIComparable). - Over-parallelizing: Parallel processing has thread management overhead. For small datasets or cheap operations, the overhead exceeds the benefit. Only parallelize CPU-bound work on large inputs.
Summary
- Profile first with
Stopwatchor BenchmarkDotNet — never guess the bottleneck - Reduce algorithmic complexity (O(n²) → O(n)) before micro-optimizing
- Choose appropriate data structures:
HashSetfor lookups,Dictionaryfor key-value,Span<T>for slicing without allocation - Minimize allocations in hot paths using
Span<T>,StringBuilder, and value types - Use
Parallel.Forfor CPU-bound work on large datasets, but avoid over-parallelizing
Related reading
- Optimizing very often used anagram function
- Optimum path in a graph to maximize a value
- Order-independent \`Hash\` Algorithm
- Order a MySQL table by two columns
- Ordered starting and waiting for containers
- OrderedDict performance compared to deque
- Optional return in C.Net
- Or equivalent in Linq Where lambda expression

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 courseTrack 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.