Why C# or VB.net Generics perform better than Collections – a case study with example

Generics are the most powerful feature of .NET 2.0. Generics not only allow us to define type-safe data structures but also gives us a significant performance boost and higher quality code. Collections means we are talking about System.Collections and Generics means we are talking about System.Collections.Generic. The data structures in System.Collections are all Object-based, and thus inheriting the two problems: performance and lack of type safety. Hence .NET 2.0 introduces a set of generic collections under System.Collections.Generic namespace to overcome these two problems mainly. For example, generic List<T> data structure is equivalent to the non-generic ArrayList. Here in this article we are going to analysis the performance between Generics(List) and Collections(ArrayList). After doing the example hope you will get a lot of confidence on using Generics instead of typical Collections.

Example Outcome:
Peformece evaluation of C# Generics over Collections

Note: Our example varies with MSDN due to different volume of data. You will get more performance benefit on large set of data. In a small set of data, you can’t measure the actual performance. Here for Value Type testing we will use 100000 data & for Reference Type 300000. Since Generics performs better on Value Type rather than Reference Type in terms of gain.

Let’s start the C# Example:
Create a project & add below namespaces:
1. using System.Collections;
2. using System.Collections.Generic;
3. using System.Diagnostics; // Used to measure the execution time of C# code blocks

write below 2 methods for testing Value Type:

        private Int32 Test_Collection_Performence()
        {
            ArrayList number_Array = new ArrayList();
            for (int i = 0; i < 100000; i++)
                number_Array.Add(i);

            Int32 nResult = 0;
            foreach (int num in number_Array)
                nResult += num;

            return nResult;
        }

        private Int32 Test_Generics_Performence()
        {
            List<int> number_Array = new List<int>();
            for (int i = 0; i < 100000; i++)
                number_Array.Add(i);

            Int32 nResult = 0;
            foreach (int num in number_Array)
                nResult += num;

            return nResult;
        }

Write below 2 methods for testing Reference Type:

        class SalesOrder
        {
            public int OrderID { get; set; }
            // Rest of the properties omitted
        }

        private void Test_Collection_Performence_by_object()
        {
            ArrayList order_Array = new ArrayList();
            for (int i = 0; i < 300000; i++)
            {
                SalesOrder objOrder = new SalesOrder();
                objOrder.OrderID = i;

                order_Array.Add(objOrder);
            }

            foreach (SalesOrder obj in order_Array)
            {
                SalesOrder SO = obj;
            }
        }

        private void Test_Generic_Performence_by_object()
        {
            List<SalesOrder> order_Array = new List<SalesOrder>();
            for (int i = 0; i < 300000; i++)
            {
                SalesOrder objOrder = new SalesOrder();
                objOrder.OrderID = i;

                order_Array.Add(objOrder);
            }

            foreach (SalesOrder obj in order_Array)
            {
                SalesOrder SO = obj;
            }
        }

Now invoke all from Form1_Load Event & Calculate the Time:

        private void Form1_Load(object sender, EventArgs e)
        {
            lblSummary.Text = "Performence testing for value type:";
            lblSummary.Text += "\n------------------------------------------";
            Stopwatch sw1 = Stopwatch.StartNew();
            Test_Collection_Performence();
            sw1.Stop();
            lblSummary.Text += string.Format("\nTime taken by System.Collections: {0}ms", sw1.Elapsed.TotalMilliseconds);

            Stopwatch sw2 = Stopwatch.StartNew();
            Test_Generics_Performence();
            sw1.Stop();
            lblSummary.Text += string.Format("\n\nTime taken by System.Collections.Generic: {0}ms", sw2.Elapsed.TotalMilliseconds);

            lblSummary.Text += "\n\nPerformence testing for ref/object type:";
            lblSummary.Text += "\n-----------------------------------------------";
            
            Stopwatch sw3 = Stopwatch.StartNew();
            Test_Collection_Performence_by_object();
            sw1.Stop();
            lblSummary.Text += string.Format("\nTime taken by System.Collections: {0}ms", sw3.Elapsed.TotalMilliseconds);

            Stopwatch sw4 = Stopwatch.StartNew();
            Test_Generic_Performence_by_object();
            sw1.Stop();
            lblSummary.Text += string.Format("\n\nTime taken by System.Collections.Generic: {0}ms", sw4.Elapsed.TotalMilliseconds);
        }

Let’s start the VB.Net Example:
Create a project & add below namespaces:
1. Imports System.Collections
2. Imports System.Collections.Generic
3. Imports System.Diagnostics // Used to measure the execution time of VB.Net code blocks

write below 2 methods for testing Value Type:

    Private Function Test_Collection_Performence() As Int64
        Dim number_Array As New ArrayList()
        For i As Integer = 0 To 99999
            number_Array.Add(i)
        Next

        Dim nResult As Int64 = 0
        For Each num As Integer In number_Array
            nResult += num
        Next

        Return nResult
    End Function

    Private Function Test_Generics_Performence() As Int64
        Dim number_Array As New List(Of Integer)()
        For i As Integer = 0 To 99999
            number_Array.Add(i)
        Next

        Dim nResult As Int64 = 0
        For Each num As Integer In number_Array
            nResult += num
        Next

        Return nResult
    End Function

Write below 2 methods for testing Reference Type:

    Private Class SalesOrder
        Public Property OrderID() As Integer
            Get
                Return m_OrderID
            End Get
            Set(value As Integer)
                m_OrderID = Value
            End Set
        End Property
        Private m_OrderID As Integer
        ' Rest of the properties omitted
    End Class

    Private Sub Test_Collection_Performence_by_object()
        Dim order_Array As New ArrayList()
        For i As Integer = 0 To 299999
            Dim objOrder As New SalesOrder()
            objOrder.OrderID = i

            order_Array.Add(objOrder)
        Next

        For Each obj As SalesOrder In order_Array
            Dim SO As SalesOrder = obj
        Next
    End Sub

    Private Sub Test_Generic_Performence_by_object()
        Dim order_Array As New List(Of SalesOrder)()
        For i As Integer = 0 To 299999
            Dim objOrder As New SalesOrder()
            objOrder.OrderID = i

            order_Array.Add(objOrder)
        Next

        For Each obj As SalesOrder In order_Array
            Dim SO As SalesOrder = obj
        Next
    End Sub

Now invoke all from Form1_Load Event & Calculate the Time:

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        lblSummary.Text = "Performence testing for value type:"
        lblSummary.Text += vbLf & "------------------------------------------"
        Dim sw1 As Stopwatch = Stopwatch.StartNew()
        Test_Collection_Performence()
        sw1.[Stop]()
        lblSummary.Text += String.Format(vbLf & "Time taken by System.Collections: {0}ms", sw1.Elapsed.TotalMilliseconds)

        Dim sw2 As Stopwatch = Stopwatch.StartNew()
        Test_Generics_Performence()
        sw1.[Stop]()
        lblSummary.Text += String.Format(vbLf & vbLf & "Time taken by System.Collections.Generic: {0}ms", sw2.Elapsed.TotalMilliseconds)

        lblSummary.Text += vbLf & vbLf & "Performence testing for ref/object type:"
        lblSummary.Text += vbLf & "-----------------------------------------------"

        Dim sw3 As Stopwatch = Stopwatch.StartNew()
        Test_Collection_Performence_by_object()
        sw1.[Stop]()
        lblSummary.Text += String.Format(vbLf & "Time taken by System.Collections: {0}ms", sw3.Elapsed.TotalMilliseconds)

        Dim sw4 As Stopwatch = Stopwatch.StartNew()
        Test_Generic_Performence_by_object()
        sw1.[Stop]()
        lblSummary.Text += String.Format(vbLf & vbLf & "Time taken by System.Collections.Generic: {0}ms", sw4.Elapsed.TotalMilliseconds)
    End Sub

Behind the scene/Reason:
Any reference or value type that is added to an ArrayList is implicitly upcast to Object. If the items are value types, they must be boxed when added to the list, and unboxed when they are retrieved. Both the casting and the boxing and unboxing operations degrade performance; the effect of boxing and unboxing can be quite significant in scenarios where you must iterate over large collections. As you have seen in above example, at the time of fetching the data from the ArrayList collection we need to do type casting which cause performance degrades. But at the time of fetching the data from the generic List we don’t required to do type casting. In this way Generics provide better performance than collections.

Now run the project. Increase the data volume. You will get better performance – promised. Not yet convinced? Download our working example:

Download Code Example C#        Download Code Example VB.Net

Posted in .Net, Asp.net C# Vb.net Interview Question, C#, VB.Net
4 comments on “Why C# or VB.net Generics perform better than Collections – a case study with example
  1. Wtf says:

    Hey, the code is really horrible, bad naming of variables and methods, bad code formatting and more importantly, your code causes int overflows. Change the value type to long if you sum up a collection of integers…! Apart from that, good point, thing is, if you care about performance that much, use arrays instead, it is much much faster private static int TestArrayPerformence() { var number_Array = new int[1000000]; for (int i = 0; i < 1000000; i++) number_Array[i] = i; var nResult = 0; foreach (int num in number_Array) nResult += num; return nResult; } by the way, on my pc collection performance is faster than generic collection for objects 😉

    • code display says:

      HI, Thanks for the comment & nice observation. Please find my view from below:
      bad naming of variables and methods, bad code formatting: Out of scope. Use google/MS guideline.
      your code causes int overflows: I didn’t get in C# but observed in VB & change the datatype accordingly. Please change it to long if overflow occurs.
      use arrays instead: We are talking about heterogeneous data structure. Array is always faster :)
      on my pc collection performance is faster: Might be. Increase data size. As much as boxing & Unboxing required, generic will perform better. For smaller set of data you can’t conclude performance issue !!

      • Wtf says:

        >> on my pc collection performance is faster: > Might be. Increase data size. As much as boxing & Unboxing required, generic will perform better. For smaller set of data you can’t conclude performance issue !! Nope, increased it to 10000000 for example, measured times are roughly the same.

Leave a Reply

Your email address will not be published. Required fields are marked *

     

*