Thursday, September 16, 2010

C# Structure vs Class

Structure performs better than the reference type (Class) with respect to CPU usage and memory usage. Consider using structure over class,
  • If your application has to support data which does not require inheritance and interface
  • The data is in large number
  • The data is only of primitive data type like integer, byte, float, etc.
  • Probability a graphics application or application which deals with large data

Note: Structure with large number of members will degrade the performance when passed as parameter/argument to functions because the values of the entire structure is copied as parameter/argument to the function.

Below code measures the creation of 100000 objects of a class and also measures the time taken to search an object from the collection.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Drawing;
namespace ClassTimeElapsed
{
    class Brick
    {
        public int Id;
        public float X;
        public float Y;
        public float Width;
        public float Height;
        public Color Color;
    }
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch objStopwatch;
            Brick[] arrBrick;
            int iCount;
            iCount = 100000;

            objStopwatch = new Stopwatch();
            objStopwatch.Reset();
            objStopwatch.Start();
            arrBrick = new Brick[iCount];

            for (int i = 0; i < iCount; i++)
            {
                arrBrick[i] = new Brick();
            }
            int iConst = 15;
            for (int i = 0; i < iCount; i++)
            {
                arrBrick[i].Id = i + 1;
                arrBrick[i].X = i * iConst;
                arrBrick[i].Y = 0;
                arrBrick[i].Width = 10;
                arrBrick[i].Height = 10;
                arrBrick[i].Color = Color.Teal;
            }
            objStopwatch.Stop();
            Console.WriteLine(
                "Class time elapsed for allocation and population");
            //timeelapsed
            Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
                objStopwatch.Elapsed.TotalMilliseconds);
            Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
                objStopwatch.ElapsedMilliseconds);
            int iSearchId = 99880;
            Brick objBrick = null;
            objStopwatch.Reset();
            objStopwatch.Start();
            for (int i = 0; i < arrBrick.Length; i++)
            {
                if (iSearchId == arrBrick[i].Id)
                {
                    objBrick = arrBrick[i];
                    break;
                }
            }
            objStopwatch.Stop();
            Console.WriteLine(
                "Class time elapsed for searching");
            //search result
            Console.WriteLine(
                "search result: Id:{0},X:{1},Y:{2},Width:{3},Height:{4},Color:{5}",
                objBrick.Id, objBrick.X, objBrick.Y, objBrick.Width, objBrick.Height,
                objBrick.Color);
            //timeelapsed
            Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
                objStopwatch.Elapsed.TotalMilliseconds);
            Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
                objStopwatch.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }
}
Output:
Class time elapsed for allocation and population
TimeElapsed (Stopwatch float):13.7327ms
TimeElapsed (Stopwatch rounded):13ms
Class time elapsed for searching
search result: Id:99880,X:1498185,Y:0,Width:10,Height:10,Color:Color [Teal]
TimeElapsed (Stopwatch float):1.3015ms
TimeElapsed (Stopwatch rounded):1ms
Below code measures the creation of 100000 objects of a struct and also measures the time taken to search an object from the collection.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Drawing;
namespace StructTimeElapsed
{
    struct Brick
    {
        public int Id;
        public float X;
        public float Y;
        public float Width;
        public float Height;
        public Color Color;
    }
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch objStopwatch;
            Brick[] arrBrick;
            int iCount;
            iCount = 100000;
            objStopwatch = new Stopwatch();
            objStopwatch.Reset();
            objStopwatch.Start();
            arrBrick = new Brick[iCount];
            int iConst = 15;
            for (int i = 0; i < iCount; i++)
            {
                arrBrick[i].Id = i + 1;
                arrBrick[i].X = i * iConst;
                arrBrick[i].Y = 0;
                arrBrick[i].Width = 10;
                arrBrick[i].Height = 10;
                arrBrick[i].Color = Color.Teal;

            }
            objStopwatch.Stop();
            Console.WriteLine(
                "Struct time elapsed for allocation and population");
            //timeelapsed
            Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
                objStopwatch.Elapsed.TotalMilliseconds);
            Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
                objStopwatch.ElapsedMilliseconds);
            int iSearchId = 99880;
            Brick objBrick;
            objBrick.Id = 0;
            objBrick.X = 0;
            objBrick.Y = 0;
            objBrick.Width = 0;
            objBrick.Height = 0;
            objBrick.Color = Color.Transparent;
            objStopwatch.Reset();
            objStopwatch.Start();
            for (int i = 0; i < arrBrick.Length; i++)
            {
                if (iSearchId == arrBrick[i].Id)
                {
                    objBrick = arrBrick[i];
                    break;
                }
            }
            objStopwatch.Stop();
            Console.WriteLine(
                "Struct time elapsed for searching");
            //search result
            Console.WriteLine(
                "search result: Id:{0},X:{1},Y:{2},Width:{3},Height:{4},Color:{5}",
                objBrick.Id, objBrick.X, objBrick.Y, objBrick.Width, objBrick.Height,
                objBrick.Color);
            //timeelapsed
            Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
                objStopwatch.Elapsed.TotalMilliseconds);
            Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
                objStopwatch.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }
}
Output:
Struct time elapsed for allocation and population
TimeElapsed (Stopwatch float):3.0684ms
TimeElapsed (Stopwatch rounded):3ms
Struct time elapsed for searching
search result: Id:99880,X:1498185,Y:0,Width:10,Height:10,Color:Color [Teal]
TimeElapsed (Stopwatch float):1.0981ms
TimeElapsed (Stopwatch rounded):1ms
Below code measures the memory usage of creation of 100000 objects of a class.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Drawing;
namespace ClassMemoryUsage
{
    class Brick
    {
        public int Id;
        public float X;
        public float Y;
        public float Width;
        public float Height;
        public Color Color;
    }
    class Program
    {
        static void Main(string[] args)
        {
            long lngGCMemStart = 0;
            long lngGCMemEnd = 0;
            long lngProcessMemStart = 0;
            long lngProcessMemEnd = 0;
            Brick[] arrBrick;
            int iCount;
            iCount = 100000;
            Process objProcess = Process.GetCurrentProcess();
            //Measure starting point memory
            lngGCMemStart = System.GC.GetTotalMemory(true);
            lngProcessMemStart = objProcess.PrivateMemorySize64;
            arrBrick = new Brick[iCount];
            for (int i = 0; i < iCount; i++)
            {
                arrBrick[i] = new Brick();
            }
            //Measure memory after allocating
            lngGCMemEnd = System.GC.GetTotalMemory(true);
            lngProcessMemEnd = objProcess.PrivateMemorySize64;
            int iConst = 15;
            for (int i = 0; i < iCount; i++)
            {
                arrBrick[i].Id = i+1;
                arrBrick[i].X = i*iConst;
                arrBrick[i].Y = 0;
                arrBrick[i].Width = 10;
                arrBrick[i].Height = 10;
                arrBrick[i].Color = Color.Teal;
            }
            Console.WriteLine("Class memory usage.");
            //memoryusage difference
            Console.WriteLine("GC Memory Use:{0} (bytes)",
                lngGCMemEnd - lngGCMemStart);
            Console.WriteLine("Process Memory Use:{0} (bytes)",
                lngProcessMemEnd - lngProcessMemStart);
            Console.WriteLine("GC Memory Start:{0} (bytes)",
                lngGCMemStart.ToString());
            Console.WriteLine("Process Memory Start:{0} (bytes)",
                lngProcessMemStart.ToString());
            Console.WriteLine("GC Memory End:{0} (bytes)",
                lngGCMemEnd.ToString());
            Console.WriteLine("Process Memory End:{0} (bytes)",
                lngProcessMemEnd.ToString());
            Console.ReadLine();
        }
    }
}
Output:
Class memory usage.
GC Memory Use:4802368 (bytes)
Process Memory Use:0 (bytes)
GC Memory Start:130648 (bytes)
Process Memory Start:9232384 (bytes)
GC Memory End:4933016 (bytes)
Process Memory End:9232384 (bytes)
Below code measures the memory usage of creation of 100000 objects of a struct.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Drawing;

namespace StructMemoryUsage
{
    struct Brick
    {
        public int Id;
        public float X;
        public float Y;
        public float Width;
        public float Height;
        public Color Color;
    }

    class Program
    {
        static void Main(string[] args)
        {
            long lngGCMemStart = 0;
            long lngGCMemEnd = 0;
            long lngProcessMemStart = 0;
            long lngProcessMemEnd = 0;
            Brick[] arrBrick;
            int iCount;
            iCount = 100000;
            Process objProcess = Process.GetCurrentProcess();
            //Measure starting point memory
            lngGCMemStart = System.GC.GetTotalMemory(true);
            lngProcessMemStart = objProcess.PrivateMemorySize64;
            arrBrick = new Brick[iCount];
            //Measure memory after allocating
            lngGCMemEnd = System.GC.GetTotalMemory(true);
            lngProcessMemEnd = objProcess.PrivateMemorySize64;
            int iConst = 15;
            for (int i = 0; i < iCount; i++)
            {
                arrBrick[i].Id = i + 1;
                arrBrick[i].X = i * iConst;
                arrBrick[i].Y = 0;
                arrBrick[i].Width = 10;
                arrBrick[i].Height = 10;
                arrBrick[i].Color = Color.Teal;
            }
            Console.WriteLine("Struct memory usage.");
            //memoryusage difference
            Console.WriteLine("GC Memory Use:{0} (bytes)",
                lngGCMemEnd - lngGCMemStart);
            Console.WriteLine("Process Memory Use:{0} (bytes)",
                lngProcessMemEnd - lngProcessMemStart);
            Console.WriteLine("GC Memory Start:{0} (bytes)",
                lngGCMemStart.ToString());
            Console.WriteLine("Process Memory Start:{0} (bytes)",
                lngProcessMemStart.ToString());
            Console.WriteLine("GC Memory End:{0} (bytes)",
                lngGCMemEnd.ToString());
            Console.WriteLine("Process Memory End:{0} (bytes)",
                lngProcessMemEnd.ToString());
            Console.ReadLine();
        }
    }
}
Output:
Struct memory usage.
GC Memory Use:3602368 (bytes)
Process Memory Use:0 (bytes)
GC Memory Start:130656 (bytes)
Process Memory Start:9232384 (bytes)
GC Memory End:3733024 (bytes)
Process Memory End:9232384 (bytes)

Why? Structure is created in the stack.Structure is a value type.

Rule: Consider using structure for supporting large number of objects(data) in your application.

No comments: