Thursday, June 17, 2010

Optimizing C# code

Let us try to understand what is called optimizing a code.
Write a simple logging function which shall
write the given string message to a file.

Step #1 - Write the Logger class with a function which will open/create the text file and append the string message along with time stamp and close the file.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Threading;

namespace LoggerTimeElapsed
{

    class Logger
    {
        public string FilePath { get; set; }
        private object m_objLocker;
        public Logger()
        {
            m_objLocker = new object();
            FilePath = @"d:\log.txt";
        }
        //make sure the StreamWriter is closed
        //make sure thr Monitor is exit
        public bool WriteToFile(string strMessage)
        {
            bool blnStatus = false;
            StreamWriter objStreamWriter = null;
            if (strMessage != string.Empty && strMessage != null)
            {
                try
                {
                    Monitor.Enter(m_objLocker);
                    objStreamWriter = new StreamWriter(FilePath, true);
                    //logs only the time and not the date
                    objStreamWriter.WriteLine(
                        DateTime.Now.TimeOfDay + " - " + strMessage
                        );
                    objStreamWriter.Close();
                    Monitor.Exit(m_objLocker);
                    blnStatus = true;
                }
                catch (Exception objEx)
                {
                    //use messagebox if winforms application
                    Console.WriteLine(objEx.StackTrace);
                    if (objStreamWriter != null)
                    {
                        objStreamWriter.Close();
                    }
                    Monitor.Exit(m_objLocker);
                }
                finally
                {
                }
            }
            return blnStatus;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Logger objLogger = null;
            Stopwatch objSW = null;
            int iCount = 100;
            objSW = new Stopwatch();
            objLogger = new Logger();
            objSW.Reset();
            objSW.Start();
            for (int i = 0; i < iCount; i++)
            {
                objLogger.WriteToFile("Unoptimized" + i.ToString());
            }
            objSW.Stop();
            Console.WriteLine("WriteToFile - TimeElapsed");
            Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
                objSW.Elapsed.TotalMilliseconds);
            Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
                objSW.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }
}
Step #2 - Measure the time taken by the function to log 100 messages to the text file.In the above code Stopwatch class is used for measuring the time.
Output:
WriteToFile - TimeElapsed
TimeElapsed (Stopwatch float):319.2352ms
TimeElapsed (Stopwatch rounded):319ms
Step #3 - If the time taken by the function does not meet the throughput requirement, then try to optimize the code. So let us now change the Logger class such that, the text file is opened only once and closed only once.
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Threading;

namespace LoggerTimeElapsed
{

    class Logger
    {
        public string FilePath { get; set; }
        private StreamWriter m_objStreamWriter = null;
        private object m_objLocker;
        public Logger()
        {
            m_objLocker = new object();
            FilePath = @"d:\log.txt";
        }
        public bool Open()
        {
            bool blnStatus = false;
            try
            {
                m_objStreamWriter = new StreamWriter(FilePath, true);
                blnStatus = true;
            }
            catch (Exception objEx)
            {
                Console.WriteLine(objEx.StackTrace);
            }
            finally
            {
            }
            return blnStatus;
        }
        public bool Close()
        {
            bool blnStatus = false;
            try
            {
                if (m_objStreamWriter != null)
                {
                    m_objStreamWriter.Close();
                    blnStatus = true;
                }
            }
            catch (Exception objEx)
            {
                Console.WriteLine(objEx.StackTrace);
            }
            finally
            {
            }
            return blnStatus;
        }
        //make sure Open is called before calling WriteToFile
        //make sure Close is called once all logging is done
        public bool WriteToFile(string strMessage)
        {
            bool blnStatus = false;
            if (strMessage != string.Empty && strMessage != null)
            {
                try
                {
                    lock (m_objLocker)
                    {
                        if (m_objStreamWriter != null)
                        {
                            //logs only the time and not the date
                            m_objStreamWriter.WriteLine(
                                DateTime.Now.TimeOfDay
                                + " - "
                                + strMessage
                                );
                            blnStatus = true;
                        }

                    }
                }
                catch (Exception objEx)
                {
                    //use messagebox if winforms application
                    Console.WriteLine(objEx.StackTrace);
                }
                finally
                {
                }
            }
            return blnStatus;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Logger objLogger = null;
            Stopwatch objSW = null;
            int iCount = 100;
            objSW = new Stopwatch();
            objLogger = new Logger();
            objLogger.Open();
            objSW.Reset();
            objSW.Start();
            for (int i = 0; i < iCount; i++)
            {
               objLogger.WriteToFile("Optimized" + i.ToString());
            }
            objSW.Stop();
            objLogger.Close();
            Console.WriteLine("WriteToFile - TimeElapsed");
            Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
                objSW.Elapsed.TotalMilliseconds);
            Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
                objSW.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }
}
Step #4 - Measure the time take by the Logger class to log 100 message to the text file.In the above code Stopwatch class is used for measuring the time.
Output:
WriteToFile - TimeElapsed
TimeElapsed (Stopwatch float):4.6166ms
TimeElapsed (Stopwatch rounded):4ms
Step #5 - Analyze the advantages and disadvantages of different approaches and decide the best approach.

Write the code → Measure → Optimize the code → Measure

No comments: