Best Practices for High Performance Code & Optimization ExamplesSome of the best coding practices to get high performance code together with a few common pitfalls which seriously impact performance.

High performance code is essential in today's connected world, with website response times, API requests and mobile app users all demanding instant response times. Here are some best practices to get the best performance and a few common pitfalls which can seriously hamper performance.
These tips should not be considered gospel. A lot of what you do will be governed by the actual scenario you are coding for. This is a list of things to be aware of when coding. A lot of the tips below relate to operations performed in a loop. If you have one string comparison in a method, you won't notice a difference when changing to a string.Compare only when you repeatedly operate in a loop many times.
In the examples below, I will compare using the Stopwatch class, which provides a set of methods and properties that you can use to measure elapsed time accurately. However, this is just a sample code to illustrate the point. It would be best to run a good profiler on your code, telling you where bottlenecks are and optimising the worst-performing methods. Keep running the profiler on each change so you can measure the difference.

This code was compiled using the release configuration and ran without the debugger - launched from the command line to eliminate any delays and bottlenecks caused by debug code and JIT debugging.
Native Types vs Generic Types
Using generic types instead of native can negatively impact high-performance code. If you only need the functionality of an array, use an array over a List. Even if you need a List, you can do some array building and convert it to a List afterwards.
using System.Diagnostics;
Stopwatch sw = new Stopwatch();
List<int> intList = new List<int>();
int[] intArray = new int[10000];
sw.Start();
for (int i = 0; i < 10000; i++)
{
intList.Add(i);
}
sw.Stop();
Console.Write("Using List<int> took " + sw.ElapsedTicks + " ticks\n");
sw.Reset();
sw.Start();
for (int i = 0; i < 10000; i++)
{
intArray[i] = i;
}
sw.Stop();
Console.Write("Using int[] took " + sw.ElapsedTicks + " ticks\n");
Using List<int> took 1622 ticks
Using int[] took 197 ticks
There is a catch with this. To initiate the int[] array, you need to know the number of elements in the array. If you don't know that value, then a Listvar list = new List<int>(10000);
. If you specify the number of elements for the List
StringBuilder Class
A string data type is immutable, meaning that once it is created, it cannot be changed. You may think you do that all the time, string name = "John"; name += " Doe";
However, what happens behind the scenes is that the CLR will create a new string object, assign it the value of the old one plus the extra data, then dispose of the old object. If you do string concatenation more than 5 or 6 times, this becomes highly inefficient, especially when doing string concatenation in a loop where each iteration worsens the performance.
The solution for high-performance code is to use the StringBuilder class, which allows the string to be manipulated without objects being created and disposed of each time. The StringBuilder can then be converted back to a string when needed.
using System.Diagnostics;
using System.Text;
Stopwatch sw = new Stopwatch();
string myString = "";
StringBuilder sb = new StringBuilder();
sw.Start();
for (int i = 0; i < 10000; i++)
{
myString += ".";
}
sw.Stop();
Console.Write("Using String Concatenate took " + sw.ElapsedTicks + " ticks\n");
sw.Reset();
sw.Start();
for (int i = 0; i < 10000; i++)
{
sb.Append(".");
}
sw.Stop();
Console.Write("Using StringBuilder took " + sw.ElapsedTicks + " ticks\n");
Using String Concatenate took 228,747 ticks
Using StringBuilder took 1127 ticks
Remember: If you are concatenating up to five strings, use the + operator or a string.format if that is appropriate. If you are constructing raw HTML, XML, CSV, etc, as a long string, then use a StringBuilder.
For vs ForEach Loop
A foreach statement is better in terms of readability and is less error-prone. However, there is a performance decrease compared with a traditional for loop. Depending on the nature of the loop and how many iterations it will be performing, consider a for loop instead of foreach.
I assign the array length to the count variable instead of using intArray.length inside the for loop initialiser. This is because each iteration of the loop will evaluate the intArray.length again. A simple array like this only helps performance a little; however, if it is something like a LINQ result or generic collection type, it can hurt performance. We know the count will not change, so there is no need to calculate it each time, and we can make our code a little faster.
using System.Diagnostics;
Stopwatch sw = new Stopwatch();
List<Int32> intList = new List<int>();
Int32[] intArray = new Int32[10000];
// Set the initial data array
for (int i = 0; i < 10000; i++)
{
intArray[i] = i;
}
sw.Start();
foreach (int i in intArray)
{
intList.Add(i);
}
sw.Stop();
Console.Write("Using foreach took " + sw.ElapsedTicks + " ticks\n");
sw.Reset();
intList.Clear();
int count = intArray.Length;
sw.Start();
for (int i = 0; i < count; i++)
{
intList.Add(intArray[i]);
}
sw.Stop();
Console.Write("Using for took " + sw.ElapsedTicks + " ticks\n");
Using foreach took 1859 ticks
Using for took 322 ticks
Parallel ForEach
While talking about loops introduced in .Net 5, Parallel ForEach and Parallel For can dramatically improve performance when a long-running task is run on each iteration. Parallel ForEach uses threads to process multiple iterations simultaneously, so it is only suited for some situations. Still, it can offer significant performance increases in a thread-safe environment.
Note: Parallel ForEach (and Parallel For) is only faster when long-running tasks exist. If the computations are simple, a regular for loop is faster. In this example, I used Thread.Sleep to simulate a long task.
using System.Diagnostics;
Stopwatch sw = new Stopwatch();
List<Int32> intList = new List<int>();
Int32[] intArray = new Int32[10000];
// Set the initial data array
for (int i = 0; i < 10000; i++)
{
intArray[i] = i;
}
sw.Start();
foreach (int i in intArray)
{
intList.Add(i);
System.Threading.Thread.Sleep(1);
}
sw.Stop();
Console.Write("Using ForEach took " + sw.ElapsedTicks + " ticks\n");
sw.Reset();
intList.Clear();
int count = intArray.Length;
sw.Start();
Parallel.ForEach(intArray, i =>
{
intList.Add(i);
System.Threading.Thread.Sleep(1);
});
sw.Stop();
Console.Write("Using Parallel.ForEach took " + sw.ElapsedTicks + " ticks\n");
Using ForEach took 1,562,276,676 ticks
Using Parallel.ForEach took 115,039,055 ticks
Classes vs Struct's
Classes and structs are two very similar types, with a few differences. Classes are reference types, structs are value types, and classes can use inheritance, whereas structs cannot.
Many developers use a class to define a group of fields with no internal logic; they are just used to hold related values. In these instances, using a struct is often far better for performance.
using System.Diagnostics;
Stopwatch sw = new Stopwatch();
MyStruct[] myStructArray = new MyStruct[10000];
MyClass[] myClassArray = new MyClass[10000];
sw.Start();
for (int i = 0; i < 10000; i++)
{
myClassArray[i] = new MyClass();
myClassArray[i].Name = "Name " + i;
myClassArray[i].Value = i;
}
sw.Stop();
Console.Write("Using class " + sw.ElapsedTicks + " ticks\n");
sw.Reset();
sw.Start();
for (int i = 0; i < 10000; i++)
{
myStructArray[i] = new MyStruct();
myStructArray[i].Name = "Name " + i;
myStructArray[i].Value = i;
}
sw.Stop();
Console.Write("Using struct " + sw.ElapsedTicks + " ticks\n");
struct MyStruct
{
public string Name { get; set; }
public int Value { get; set; }
}
class MyClass
{
public string Name { get; set; }
public int Value { get; set; }
}
Using class 10,542 ticks
Using struct 7769 ticks
The caveat with this is memory usage. Classes are reference types, so passing around references between methods is easy. Structures are value types, so the object is cloned and copied. Again, depending on what you do with the data, a structure may be faster and more efficient.
Property Assignment vs Direct Field Assignment
Using properties is considered good practice, and Visual Studio will prompt you to create them automatically; however, using properties for fields having no logic can create performance hits. By all means, if you need to run some logic on the getter or setter, then use properties, but if you're storing and retrieving a value, then it can pay to use and access the field directly.
using System.Diagnostics;
Stopwatch sw = new Stopwatch();
MyStructProperty[] myStructPropertyArray = new MyStructProperty[10000];
MyStructDirect[] myStructDirectArray = new MyStructDirect[10000];
sw.Start();
for (int i = 0; i < 10000; i++)
{
myStructPropertyArray[i] = new MyStructProperty();
myStructPropertyArray[i].Name = "Name " + i;
myStructPropertyArray[i].Value = i;
}
sw.Stop();
Console.Write("Using property get/set " + sw.ElapsedTicks + " ticks\n");
sw.Reset();
sw.Start();
for (int i = 0; i < 10000; i++)
{
myStructDirectArray[i] = new MyStructDirect();
myStructDirectArray[i].Name = "Name " + i;
myStructDirectArray[i].Value = i;
}
sw.Stop();
Console.Write("Using direct access " + sw.ElapsedTicks + " ticks\n");
struct MyStructProperty
{
public string Name { get; set; }
public int Value { get; set; }
}
struct MyStructDirect
{
public string Name;
public int Value;
}
Using property get/set 8815 ticks
Using direct access 5263 ticks
String Comparision
When comparing two strings, most developers convert the string to lowercase (or upper) and use the == operator to compare. However, there is a much faster in-built method which is highly optimised. I am using the string.Compare
method takes care of the case for you and performs a culture-aware linguistic comparison.
using System.Diagnostics;
Stopwatch sw = new Stopwatch();
var test = false;
var string1 = "String1";
var string2 = "sTring2";
sw.Start();
for (int i = 0; i < 10000; i++)
{
test = string1.ToLower() == string2.ToLower();
}
sw.Stop();
Console.Write("Using == " + sw.ElapsedTicks + " ticks\n");
sw.Reset();
sw.Start();
for (int i = 0; i < 10000; i++)
{
test = string.Compare(string1, string2, true) == 0;
}
sw.Stop();
Console.Write("Using string.Compare " + sw.ElapsedTicks + " ticks\n");
Using == 190,271 ticks
Using string.Compare 13,780 ticks
Few Quick Bonus Tips
- Don't use magic numbers; use enumerations
- Don't hard-code values
- Use generics where possible since it's typesafe & avoids boxing & unboxing
- Use an error handler where it's absolutely needed
- Dispose, dispose, dispose. CLR won't know how to close your database connections, so close them after use and dispose of unmanaged resources
- Use common-sense!