Three Practically Used Ways To Improve The Performance Of C# Code

Introduction

There are plenty of ways to improve the performance of the C# code. Here, in this article, we will see how to use three top methods to improve the performance using some tweaks and see the result of the change. 

  • Materializing LINQ Queries Once
  • Loop Parallelization
  • Use Struct Instead of Class wherever possible

Materializing LINQ Queries Once

(Don’t use Expression tree to hold the result but extract the result by executing it, whenever possible)

When writing LINQ queries using IEnumerable or IQueryable interfaces, developers can materialize (call ToListToArray or similar methods) or not to materialize queries immediately (Lazy loading).

The lack of materialization allows developers to work with collections lazily. But sometimes it becomes necessary to iterate over the same collection several times. If the query was not materialized by calling ToList/ToArray method, iterating over the collection multiple times will affect the performance of the application.

Here is a sample where we are testing the performance of a lazy loading LINQ query vs materialized query.

[MemoryDiagnoser]
public class LinqTest {
    [Benchmark]
    public void NoMaterializedTest() {
            var list = Enumerable.Range(0, 50000000);
            var filteredlist = list.Where(element => element % 100000 == 0);
            foreach(var element in filteredlist) {
                //some logic here
            }
            foreach(var element in filteredlist) {
                // some logic here
            }
            foreach(var element in filteredlist) {
                // some logic here
            }
        }
        [Benchmark]
    public void MaterializedTest() {
        var list = Enumerable.Range(0, 5000000);
        var filteredlist = list.Where(element => element % 10000 == 0).ToList();
        //ToList() method will execute the expression tree and get the result for future purpose.
        foreach(var element in filteredlist) {
            // some logic here
        }
        foreach(var element in filteredlist) {
            // some logic here
        }
        foreach(var element in filteredlist) {
            // some logic here
        }
    }
    public static void Main() {
        //Execute LinqTest class methods to check the benchmark
        var result = BenchmarkRunner.Run < LinqTest > ();
    }

Here you can see the difference, Materialized queries perform three times better compared to lazy load LINQ queries. Although this approach will not work always as we might not execute the same result set multiple times. Always check the case before materializing the LINQ query.

Loop Parallelization

Foreach is the most commonly used loop nowadays after For loop. It gives flexibility to not worry about the loop count and it will run to the length of the collection.

In this example, iterations are performed one after the other in the same thread, so the total execution time will grow linearly with the collection size.

The performance can be improved a lot by using the parallel version of the foreach loop that framework 4.0 provides to developers.

Parallel. Foreach can be used on any collection that implements the IEnumerable<T> interface like a regular foreach loop. The implementation of a Parallel. Foreach will do a lot of work instead of the developer: it split the collection into chunks according to the core available on the machine, schedule and execute chunks in separate threads and combine the result.

Here is a performance comparison between the parallel versus the plain version of the foreach loop:

Improve the performance of Csharp Code

If collections are small and the execution time of a single iteration is fast, switching foreach to Parallel. Foreach might even get the performance worse because it adds a cost of managing the loop by splitting and collecting the result.

Use Struct Instead of Class wherever possible

I will not discuss the feature and uses of Struct here in this article but most of the time I have seen developers create classes without doing a good comparison between Class and Struct. Developers often might need to allocate an array or List<T> to store tens of thousands of objects in memory. This task can be solved using class or struct.

As we can see the only difference between ListOfClassObjectTest and ListOfStructsObjectTest is that the first test creates instances of class while the second one creates instances of structs. The code of PointClass and PointStruct is identical:-

//Reference type. Stored on Heap.
public class PointClass {
    public int X {
        get;
        set;
    }
    public int Y {
        get;
        set;
    }
}
//Value type. Stored on Stack
public struct PointStruct {
    public int X {
        get;
        set;
    }
    public int Y {
        get;
        set;
    }
}
[MemoryDiagnoser]
public class ClassVsStruct {
    [Benchmark]
    public void ListOfClassObjectsTest() {
            const int length = 1000000;
            var items = new List < PointClass > (length);
            for (int i = 0; i < length; i++) {
                items.Add(new PointClass() {
                    X = i, Y = i
                });
            }
        }
        [Benchmark]
    public void ListOfStructsObjectTest() {
        const int length = 1000000;
        var items = new List < PointStruct > (length);
        for (int i = 0; i < length; i++) {
            items.Add(new PointStruct() {
                X = i, Y = i
            });
        }
    }
}

Improve the performance of Csharp Code

Code that uses structs runs 15 times faster than code that uses classes. In the case of classes, the CLR has to allocate one million objects to the managed heap and store their references back to the List<T> collection. In the case of structs, there will be the only object allocated into a managed heap which is the instance of a List<T> collection. One million structs will be embedded into that single collection instance.

Summary

Here we have seen the performance improvement with small changes in the existing code. I will also update more in this article in the coming days.