Performance of a C# Application

 

First of all, let's define what application performance is. 

In our scenario, we can define performance or benchmarking as the speed at which a section of code, whether an algorithm or several methods, is executed on our system.

For this, we will use a benchmarking library available on NuGet called "BenchmarkDotNet

benchmarkdotnet

Once we have our library, we can check the performance. But for that, we first need to create our use case.

 

Use Case

In this scenario, I have created a very simple example, where we have a generic class -https://www.netmentor.es/Entrada/generics-csharp- that compares two lists of elements:

public bool EqualsGeneric(IList<long> x, IList<long> y)
{
    if (ReferenceEquals(x, y))
        return true;
    if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
        return false;
    if (x.Count != y.Count)
        return false;
    for (var i = 0; i < x.Count; i++)
    {
        if (x[i] == null)
        {
            if (y[i] != null)
                return false;
        }
        else if(!x[i].Equals(y[i]))
        {
            return false;
        }
    }
    return true;
}

In this particular case, we are going to compare two lists of long elements, so we can change the code to the following:

public bool EqualsLong(IList<long> x, IList<long> y)
{
    if (ReferenceEquals(x, y))
        return true;
    if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
        return false;
    if (x.Count != y.Count)
        return false;
    for (var i = 0; i < x.Count; i++)
    {
        if (!x[i].Equals(y[i]))
        {
            return false;
        }
    }
    return true;
}

As we can see, we have removed two if conditionals. This means that when we use the generic comparer, we lose a bit of performance. Since long cannot be null, both checks are unnecessary in the case of longs, but necessary for string.

 

Setting up the Scenario

In this post, we are going to see how to check performance. To do this, we create a class that contains both methods, the generic comparer and the normal comparer - we have modified the generic to make it more understandable -.

public class Ejemplo
{
    public bool EqualsGeneric(IList<long> x, IList<long> y)
    {
        if (ReferenceEquals(x, y))
            return true;
        if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
            return false;
        if (x.Count != y.Count)
            return false;
        for (var i = 0; i < x.Count; i++)
        {
            if (x[i] == null)
            {
                if (y[i] != null)
                    return false;
            }
            else if(!x[i].Equals(y[i]))
            {
                return false;
            }
        }
        return true;
    }

    public bool EqualsLong(IList<long> x, IList<long> y)
    {
        if (ReferenceEquals(x, y))
            return true;
        if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
            return false;
        if (x.Count != y.Count)
            return false;
        for (var i = 0; i < x.Count; i++)
        {
            if (!x[i].Equals(y[i]))
            {
                return false;
            }
        }
        return true;
    }
}

For our example, we are going to create two lists, each with one million elements. In the second list, we simply change the last element so that it is different, and this way we test the longest path in each of the methods. 

public IList<long> Items { get; set; }
public IList<long> ItemsList2 { get; set; }

public Ejemplo()
{
    Items = Enumerable.Range(1, 1000000).Select(a => Convert.ToInt64(a)).ToList();
    ItemsList2 = Enumerable.Range(1, 1000000-1).Select(a => Convert.ToInt64(a)).ToList();
    ItemsList2.Add(1);
}

We must instantiate both lists in the constructor, since to avoid any delay in the code execution, or the result to see which one is better, the benchmark library advises that we should not send dynamic elements to the functions. 

Finally, we must create the two methods that we will use to compare both lists.

public void BenchmarkGenericEquals()
{
    _ = EqualsGeneric(Items, ItemsList2);
}

public void BenchmarkLongEquals()
{
    _ = EqualsLong(Items, ItemsList2);
}

 

Benchmarking

Now it's time to use the library we installed. 

For this, we need to add the [Benchmark] attribute to each of the methods we want to test.

using BenchmarkDotNet.Running;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Attributes;

[Benchmark]
public void BenchmarkGenericEquals()
{
    _ = EqualsGeneric(Items, ItemsList2);
}

[Benchmark]
public void BenchmarkLongEquals()
{
    _ = EqualsLong(Items, ItemsList2);
}

Finally, we need to indicate that we want to actually perform the benchmark. To do so, in our main class, in our case the main method of our console application, we need to instantiate the BenchmarkRunner as follows: 

Summary summary = BenchmarkRunner.Run<Ejemplo>();

To print the results, we just need to display it to the screen, simply by passing the summary to a Console.WriteLine:

Console.WriteLine(summary);

If we run the program, the benchmark will start running, but you should keep in mind that our benchmark will execute multiple tests, not just one, so our checks will be more reliable than if we use StopWatch, which is what I personally have used until now. 

Once it's finished, which may take a while depending on the complexity of the algorithm, we should mainly focus on the final part: 

benchmark result

As we can see, it shows the method, the average execution time, the error margin, and the deviation. These figures can be modified as explained in the documentation.

Finally, the result shows that the generic method, as expected, is a bit slower than the one built specifically for long.

 

Conclusion

  • If we want to check performance, it's recommended to use this benchmark library instead of a manual StopWatch.
  • Improving performance can be recommended, but it is not always required, since making code faster can sometimes make it harder to read.
  • Even so, when in doubt between two processes, it is highly recommended to go with the one that gives us the best performance. 
This post was translated from Spanish. You can see the original one here.
If there is any problem you can add a comment bellow or contact me in the website's contact form

Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2025 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café