Saturday, February 25, 2012
Scalability & Perfoamce
1) Use DNS (Domain Name Server) to distribute your website load.
2) Use Load Balancing Switch to distribute your website load.
The above mentioned load balancing methods work very well for static websites. But if your website have to do back end processing with a database or any other service, then even those services have to be load balanced otherwise they would become the bottle neck.
The above stuff is just the beginning of the vast topic called "Scalability"!
Abstraction interface
1) System abstraction interface help in hiding the complexity of the actual system and also the changes with in the system transparent to users of the system like hardware upgrade or adding hardware resource to scale up....
Scalability on the client
1) Paging concept (showing only few mail subjects on a mail client)
Scalability on the web/app server
1) multiple threads to handle http request
2) Load balancing architecture (one load balancing proxy server with multiple app server) to handle http request
External resources
Let's make the web faster
Web browser capabilities
Online web page testing
High Performance Web Sites and YSlow
Even Faster Websites
steve souders
to be continued.....
Tuesday, May 10, 2011
Simple javascript optimizations
Rule: Consider using local variable instead of global variable.
Rule: Avoid using eval() or function constructor.
Rule: Pass function, not string to setTimeout() and setInterval(). Passing stirng is same as calling eval().
Rule: Avoid using with().
For-in loop vs for loop
Slow:function forinloop()
{
var count=10000;
var arr=new Array();
for(var i=0;i<count;i++)
{
arr[i]=i;
}
var value=0;
for(var index in arr)
{
value+=arr[index];
}
}
Faster:function forloop()
{
var count=10000;
var arr=new Array();
for(var i=0;i<count;i++)
{
arr[i]=i;
}
var value=0;
count= arr.length;
for(var index=0;index<count;index++)
{
value+=arr[index];
}
}
Rule: Avoid using for-in loop, instead use for() loop.
String concatenation vs array join
Slow:function stringcat()
{
var count=10000;
var str="";
for(var i=0;i<count;i++)
{
str+="first,";
}
}
Faster:function arrayjoin()
{
var count=10000;
var arr=new Array();
for(var i=0;i<count;i++)
{
arr[i]="first";
}
var str=arr.join("");
}
Rule: Avoid string concatenation and use array join. The performance difference in significant only in IE 7.
Inbuilt functions vs primitive operations
Slow:function mathmin()
{
var count=10000;
for(var i=0;i<count;i++)
{
var a=10;
var b=5;
var min = Math.min(a, b);
}
}
Faster:function primitiveop()
{
var count=10000;
for(var i=0;i<count;i++)
{
var a=10;
var b=5;
var min = a < b ? a : b;
}
}
Rule: Avoid function calls, try to use primitive operations. The performance difference in significant only in IE 7.
External function vs inline function
Slow:function minfunction(a,b)
{
var min = a < b ? a : b;
return min;
}
function externalfunction()
{
var count=10000;
for(var i=0;i<count;i++)
{
var a=10;
var b=5;
var min=minfunction(a,b);
}
}
Faster:function inlinefunction()
{
var count=10000;
for(var i=0;i<count;i++)
{
var a=10;
var b=5;
var min = a < b ? a : b;
}
}
Rule: Avoid external function calls, try to use inline functions. The performance difference in significant only in IE 7.
Comments in the script
Slow:function withcomments()
{
var count=50000;
for(var i=0;i<count;i++)
{
/* dummy comments */
/* dummy comments */
var a=10;
/* dummy comments */
/* dummy comments */
var b=5;
/* dummy comments */
/* dummy comments */
var min = a < b ? a : b;
/* dummy comments */
}
}
Faster:function withoutcomments()
{
var count=50000;
for(var i=0;i<count;i++)
{
var a=10;
var b=5;
var min = a < b ? a : b;
}
}
Rule: Avoid comments in performance critical script. The performance difference in significant only in IE 7.
Try catch in performance critical functions
Slow:function withtrycatch()
{
var count=50000;
for(var i=0;i<count;i++)
{
try
{
var num=parseInt("10");
}
catch(e)
{
//handle exception
}
finally
{
}
}
}
Faster:function withouttrycatch()
{
var count=50000;
for(var i=0;i<count;i++)
{
var num=parseInt("10");
}
}
Rule: Avoid using try catch inside performance critical function.
Monday, April 25, 2011
Search Engine Optimization (SEO)
Developing a website or web application which can be indexed by search engine is very critical and it is called searching engine optimization of the website or web application.
Before starting the optimization let us look at some of the basic rules to be followed
Rule: Focus on the originality and quality of the content of your website.
Rule: Remember your ultimate consumers are your users, not search engines.
Rule: Original content is ranked high in the search result page.
Rule: Increasing the traffic to your website is more important than the rank of the page in the search result.
Rule: Perform any kind of optimization only to improve the user experience.
Below are some of the critical optimization rules
Rule: Use brief, but descriptive title in "title" (<title>Search engine optimization<title/>) tag. Choose a title that effectively communicates the topic of the page's content.
Rule: Create unique title for each page of the website.
Rule: Google search engine does not use keywords "meta" tag (<meta name="keywords" content="search engine optimization, indexing, optimization, rules" />) for indexing.
Rule: Provide crisp and clear description of the webpage in the description "meta" tag (<meta name="description" content="Critical rules for search engine optimization." />). Also provide unique description for each page of the website.
Rule: Use | (pipe) or - (hyphen) as separator and not _ (underscore) in "title" tag and description "meta" tag.
Rule: Provide URLs with readable text (e.g. http://codingrules.blogspot.com/2010/06/unit-testing.html) or keywords to access a webpage and not auto generated text or numbers (e.g. http://codingrules.blogspot.com/2010/06/20006.html).
Rule: Regularly check for broken links and remove the same.
Rule: Provide naturally flowing hierarchy for navigation and text for navigation. Avoid drop down menu, images or animation for navigation.
Rule: Provide site map for the user.
Rule: Submit your website's site map (xml format) with Google's webmaster tools for search engine to index your website. Generate XML site map for your website from http://www.xml-sitemaps.com/. Also Google's webmaster tools help webmasters better control how Google interacts with their websites and get useful information from Google about their site.
Rule: Provide custom 404 page, which can guide user back to a working page.
Rule: Write easy to read text content. Avoid images for textual content.
Rule: Try to use words and phrases on the webpage which the user would be looking for.
Rule: Use descriptive anchor text for links.
Rule: Format links to make it easy for users to distinguish between regular text and the anchor text of links.
Rule: Links on your page maybe internal—pointing to other pages on your site—or external—leading to content on other sites. In either of these cases provide descriptive anchor text for better user experience. Anchor text is the clickable text that users will see as a result of a link,
and is placed within the anchor tag <a href="..."></a>.
Rule: Do not design you entire webpage (or website) as image. Provide some text on the webpage which can be indexed by search engines.
Rule: Use descriptive name for images and not like "img1.png" or "pic.png".
Rule: Provide "alt" and "title" attribute text in the "img" tag (<img alt="" title="" src="" />).
Rule: Use "heading" tags (h1 to h6) to emphasize important text.
Rule: If the company name or the website name is embedded as image, then make sure the same is made available as text in the webpage (e.g. "title" tag or description "meta" tag etc).
Rule: For webpages with photo gallery, apart from providing image "alt" and "title" text, let the users to provide comments, which will increase the textual content of the webpage. But make sure to moderate the user comments.
Rule: User comments section can improve the textual content of the webpage. Moderate the user comments to avoid spam content.
Rule: Provide "Related links" section of links with in your website to increase the traffic to your website.
Rule: Provide complete address of your company on the website either on one of the webpage or on all the pages.
Rule: Use the company/product/service information, where ever applicable, instead of using we or our.
Rule: Use Google places, Videos, Images, Facebook, Twitter, etc for promoting your website.
Watch the below videos for more on SEO.
Saturday, September 18, 2010
Simple javascript DHTML optimizations
Caching the DOM element
function nodomcache()
{
var count=1000;
for(var i=0;i<count;i++)
{
var value1=document.getElementById("div1").innerHTML;
var value2=document.getElementById("div2").innerHTML;
var value3=document.getElementById("div3").innerHTML;
}
}
Faster:function domcache()
{
var count=1000;
var ele1=document.getElementById("div1");
var ele2=document.getElementById("div2");
var ele3=document.getElementById("div3");
for(var i=0;i<count;i++)
{
var value1=ele1.innerHTML;
var value2=ele2.innerHTML;
var value3=ele3.innerHTML;
}
}
Rule: Cache the element for better performance. This caching is applicable for all types or variables.
Creating dynamic HTML (appendChild vs innerHTML)
Slow:function appendchildhtml()
{
var count=100;
var tblele=document.createElement("table");
var tblbodyele=document.createElement("tbody");
for(var i=0;i<count;i++)
{
var trele= document.createElement("tr");
var tdele1= document.createElement("td");
tdele1.appendChild(document.createTextNode(i))
trele.appendChild(tdele1);
var tdele2= document.createElement("td");
tdele2.appendChild(document.createTextNode(i))
trele.appendChild(tdele2);
tblbodyele.appendChild(trele);
}
tblele.appendChild(tblbodyele);
document.getElementById("result1").appendChild(tblele);
}
Faster:function innerhtml()
{
var count=100;
var strHTML="";
strHTML+="<table>";
for(var i=0;i<count;i++)
{
strHTML+="<tr>";
strHTML+="<td>";
strHTML+=i;
strHTML+="</td>";
strHTML+="<td>";
strHTML+=i;
strHTML+="</td>";
strHTML+="</tr>";
}
strHTML+="</table>";
document.getElementById("result2").innerHTML=strHTML;
}
Rule: Use innerHTML for dynamically adding html content.
Friday, September 17, 2010
Javascript measurement tools
- Time taken to complete some operation
- Memory usage of the application
<html>
<head>
<title>Time measurement tool</title>
<script>
function measureTimeElapsed()
{
var divlogoutput;
var starttime;
var endtime;
var timeelapsed;
starttime=(new Date()).getTime();
for(var i=0;i<10000;i++)
{
var temp=parseInt("10");
}
endtime=(new Date()).getTime();
timeelapsed=endtime-starttime;
divlogoutput=document.getElementById("logoutput");
//append the timeelapsed
divlogoutput.innerHTML="<b>TimeElapsed:"+
timeelapsed+"(milliseconds)</b><br/>";
}
</script>
</head>
<body onload="measureTimeElapsed()">
<div id="logoutput">
</div>
</body>
</html>
Output: Chrome 6.0:TimeElapsed:1(milliseconds) Safari 3.2:TimeElapsed:10(milliseconds) IE 7.0:TimeElapsed:27(milliseconds) Mozilla Firefox:3.0.3:1(milliseconds)
Note: Profiling tools can be used for the measuring execution time and memory leak.
Note: If you are testing with Google Chrome or Mozilla Firefox then try console.time(name); and console.timeEnd(name);. Please refer Console API
Thursday, September 16, 2010
C# XmlWriter vs XmlDocument vs StreamWriter
- Contact class is used for storing name and address
- AddressBook is the colleciton of Contact class
<AddressBook>
<Contact>
<Name>Person1</Name>
<Address1><![CDATA[Number101]]></Address1>
<Address2><![CDATA[Address501]]></Address2>
<Zip>50001</Zip>
<Phone><![CDATA[999991]]></Phone>
</Contact>
</AddressBook>
The example first creates and populates an AddressBook class with 10000 Contact objects and measures time taken to generate the xml file with 10000 contacts using using XmlDocument, XmlWriter and StreamWriter (usign StringBuilder).using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.Xml.Linq;
namespace XmlWrite
{
class Contact
{
public string Name { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public int Zip { get; set; }
public string Phone { get; set; }
public Contact(string strName, string strAddress1, string strAddress2,
int iZip,
string strPhone)
{
Name = strName;
Address1 = strAddress1;
Address2 = strAddress2;
Zip = iZip;
Phone = strPhone;
}
}
class AddressBook
{
private List<Contact> InnerList = null;
public List<Contact> GetContacts()
{
return InnerList;
}
public AddressBook()
{
InnerList = new List<Contact>();
}
public void Add(Contact objContact)
{
InnerList.Add(objContact);
}
}
class Program
{
static void Main(string[] args)
{
Stopwatch objSW = null;
XmlDocument objXmlDoc = null;
XmlWriter objXmlWriter = null;
XDocument objXDoc = null;
StringBuilder objSB = null;
StreamWriter objStreamWriter = null;
AddressBook objAddrBook = null;
int iCount = 10000;
List<Contact> objContacts = null;
//populate contacts in the addressbook object model
objAddrBook = new AddressBook();
objSW = new Stopwatch();
for (int i = 0; i < iCount; i++)
{
Contact objContact = null;
string strName = "Person" + (i + 1);
string strAddress1 = "Number" + (i + 101);
string strAddress2 = "Address" + (i + 501);
int iZip = 50000 + (i + 1);
string strPhone = "99999" + (i + 1);
objContact = new Contact(strName, strAddress1, strAddress2, iZip,
strPhone);
objAddrBook.Add(objContact);
}
//xml creation using XmlDocument
objSW.Reset();
objSW.Start();
objXmlDoc = new XmlDocument();
XmlElement objXmlAddrBook = objXmlDoc.CreateElement("AddressBook");
objContacts = objAddrBook.GetContacts();
for (int i = 0; i < objContacts.Count; i++)
{
XmlElement objXmlContact = objXmlDoc.CreateElement("Contact");
XmlElement objXmlName = objXmlDoc.CreateElement("Name");
objXmlName.InnerText = objContacts[i].Name;
objXmlContact.AppendChild(objXmlName);
XmlElement objXmlAddress1 = objXmlDoc.CreateElement("Address1");
objXmlAddress1.AppendChild(
objXmlDoc.CreateCDataSection(objContacts[i].Address1));
objXmlContact.AppendChild(objXmlAddress1);
XmlElement objXmlAddress2 = objXmlDoc.CreateElement("Address2");
objXmlAddress2.AppendChild(
objXmlDoc.CreateCDataSection(objContacts[i].Address2));
objXmlContact.AppendChild(objXmlAddress2);
XmlElement objXmlZip = objXmlDoc.CreateElement("Zip");
objXmlZip.InnerText = objContacts[i].Zip.ToString();
objXmlContact.AppendChild(objXmlZip);
XmlElement objXmlPhone = objXmlDoc.CreateElement("Phone");
objXmlPhone.AppendChild(
objXmlDoc.CreateCDataSection(objContacts[i].Phone));
objXmlContact.AppendChild(objXmlPhone);
objXmlAddrBook.AppendChild(objXmlContact);
}
objXmlDoc.AppendChild(objXmlAddrBook);
objXmlDoc.Save("address_dom.xml");
objSW.Stop();
Console.WriteLine("XML file created using XmlDocument");
Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds);
Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds);
//xml creation using XmlWriter
objSW.Reset();
objSW.Start();
XmlWriterSettings objSettings = new XmlWriterSettings();
objSettings.Indent = true;
using (objXmlWriter = XmlWriter.Create("address_xw.xml", objSettings))
{
objXmlWriter.WriteStartElement("AddressBook");
objContacts = objAddrBook.GetContacts();
for (int i = 0; i < objContacts.Count; i++)
{
objXmlWriter.WriteStartElement("Contact");
objXmlWriter.WriteStartElement("Name");
objXmlWriter.WriteString(objContacts[i].Name);
objXmlWriter.WriteEndElement();
objXmlWriter.WriteStartElement("Address1");
objXmlWriter.WriteCData(objContacts[i].Address1);
objXmlWriter.WriteEndElement();
objXmlWriter.WriteStartElement("Address2");
objXmlWriter.WriteCData(objContacts[i].Address2);
objXmlWriter.WriteEndElement();
objXmlWriter.WriteStartElement("Zip");
objXmlWriter.WriteString(objContacts[i].Zip.ToString());
objXmlWriter.WriteEndElement();
objXmlWriter.WriteStartElement("Phone");
objXmlWriter.WriteCData(objContacts[i].Phone);
objXmlWriter.WriteEndElement();
objXmlWriter.WriteEndElement();
}
objXmlWriter.WriteEndElement();
}
objSW.Stop();
Console.WriteLine("XML file created using XmlWriter");
Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds);
Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds);
//xml creation using StringBuilder
objSW.Reset();
objSW.Start();
objSB = new StringBuilder();
objSB.AppendLine("<AddressBook>");
objContacts = objAddrBook.GetContacts();
for (int i = 0; i < objContacts.Count; i++)
{
objSB.AppendLine("<Contact>");
objSB.AppendLine("<Name>" + objContacts[i].Name + "</Name>");
objSB.AppendLine(
"<Address1><![CDATA[" + objContacts[i].Address1 + "]]></Address1>");
objSB.AppendLine(
"<Address2><![CDATA[" + objContacts[i].Address2 + "]]></Address2>");
objSB.AppendLine("<Zip>" + objContacts[i].Zip + "</Zip>");
objSB.AppendLine(
"<Phone><![CDATA[" + objContacts[i].Phone + "]]></Phone>");
objSB.AppendLine("</Contact>");
}
objSB.AppendLine("</AddressBook>");
using (objStreamWriter = new StreamWriter("address_sb.xml", false))
{
objStreamWriter.Write(objSB.ToString());
}
objSW.Stop();
Console.WriteLine("XML file created using StringBuilder");
Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds);
Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds);
//xml creation using XDocument (LINQ)
objSW.Reset();
objSW.Start();
objContacts = objAddrBook.GetContacts();
XElement[] arrXElem = new XElement[objContacts.Count];
for (int i = 0; i < objContacts.Count; i++)
{
arrXElem[i] = new XElement("Contact",
new XElement("Name", objContacts[i].Name),
new XElement("Address1",
new XCData(objContacts[i].Address1)),
new XElement("Address2",
new XCData(objContacts[i].Address2)),
new XElement("Zip", objContacts[i].Zip),
new XElement("Phone", new XCData(objContacts[i].Phone)));
}
objXDoc = new XDocument(new XElement("AddressBook",arrXElem));
objXDoc.Save("address_xdocument.xml");
objSW.Stop();
Console.WriteLine("XML file created using XDocument");
Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds);
Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
Output: XML file created using XmlDocument TimeElapsed (Stopwatch float):90.0323ms TimeElapsed (Stopwatch rounded):90ms XML file created using XmlWriter TimeElapsed (Stopwatch float):36.2555ms TimeElapsed (Stopwatch rounded):36ms XML file created using StringBuilder TimeElapsed (Stopwatch float):91.5911ms TimeElapsed (Stopwatch rounded):91ms XML file created using XDocument TimeElapsed (Stopwatch float):71.3794ms TimeElapsed (Stopwatch rounded):71ms
Note: The StringBuilder performance can be improved by specifying the required capacity when creating the StringBuilder object.
Note: XmlDocument can be considered, if forward and backward reference is unavoidable during the generation of xml document.
Rule: Use XmlWriter for generating or creating xml document.
C# Structure vs 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.
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):1msBelow 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):1msBelow 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.
C# StringBuilder vs String Concatenation
Below code measures the time taken to concatenate a string for 5000 iteration using “string” and “StringBuilder”.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace StringVsStringBuilder
{
class Program
{
static void Main(string[] args)
{
Stopwatch objStopwatch = new Stopwatch();
string strValue=string.Empty;
StringBuilder objSBValue = new StringBuilder();
int iCount = 5000;
objStopwatch.Reset();
objStopwatch.Start();
for (int i = 0; i < iCount; i++)
{
strValue = strValue+"simpleValue"+i.ToString();
}
objStopwatch.Stop();
Console.WriteLine("concatenation using String.");
Console.WriteLine(
"TimeElapsed (Stopwatch float):{0}ms",
objStopwatch.Elapsed.TotalMilliseconds
);
Console.WriteLine(
"TimeElapsed (Stopwatch rounded):{0}ms",
objStopwatch.ElapsedMilliseconds
);
//Console.WriteLine(strValue);
objStopwatch.Reset();
objStopwatch.Start();
for (int i = 0; i < iCount; i++)
{
objSBValue.Append("simpleValue" + i.ToString());
}
objStopwatch.Stop();
Console.WriteLine(
"concatenation using StringBuilder."
);
Console.WriteLine(
"TimeElapsed (Stopwatch float):{0}ms",
objStopwatch.Elapsed.TotalMilliseconds
);
Console.WriteLine(
"TimeElapsed (Stopwatch rounded):{0}ms",
objStopwatch.ElapsedMilliseconds
);
//Console.WriteLine(objSBValue);
Console.ReadLine();
}
}
}
Output: concatenation using String. TimeElapsed (Stopwatch float):316.6526ms TimeElapsed (Stopwatch rounded):316ms concatenation using StringBuilder. TimeElapsed (Stopwatch float):2.4933ms TimeElapsed (Stopwatch rounded):2ms
Why? Any object stored in heap is immutable with respect to size allocation, and any change to the underlying size will require reallocation. If the string is modified (concatenating another string onto it, changing its value), then a new string is created and this can have negative performance implications. The StringBuilder gets around the reallocation problem by using internal buffers. But once the buffer fills up, the reallocation occurs. It is recommended to specify the initial capacity to minimize the reallocation problem.
Rule: Use StringBuilder instead of string for string concatenation operation.
C# XmlReader vs XDocument vs XmlDocument
Create an xml file as show below, but with 10000 contact elements called as “address_xw_n.xml” and make it available in D driver (d:\).
<AddressBook>
<Contact>
<Name>Person1</Name>
<Address1><![CDATA[Number101]]></Address1>
<Address2><![CDATA[Address501]]></Address2>
<Zip>50001</Zip>
<Phone><![CDATA[999991]]></Phone>
</Contact>
</AddressBook>
The below code demonstrate the time take to parse the above created xml file using XmlDocument, XDocument and XmlReader.using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Diagnostics;
using System.Xml.Linq;
namespace XmlParse
{
class Program
{
static void Main(string[] args)
{
Stopwatch objSW = null;
String strNormalFilePath;
strNormalFilePath = @"d:\address_xw_n.xml";
XmlDocument xmlNDoc;
XmlReader xmlNReader = null;
XDocument xNDoc=null;
objSW = new Stopwatch();
//xml parsing using XmlDocument - Normal format
objSW.Reset();
objSW.Start();
xmlNDoc = new XmlDocument();
xmlNDoc.Load(strNormalFilePath);
XmlElement xmlDocNElem = xmlNDoc.DocumentElement;
for (int i = 0; i < xmlDocNElem.ChildNodes.Count; i++)
{
XmlNode xmlNode = xmlDocNElem.ChildNodes[i];
for (int j = 0; j < xmlNode.ChildNodes.Count; j++)
{
string strName = xmlNode.ChildNodes[j].Name;
string strValue = xmlNode.ChildNodes[j].ChildNodes[0].Value;
//Console.WriteLine(strName + "-" + strValue);
}
}
//xmlNDoc = null;
objSW.Stop();
Console.WriteLine("XML parsing using XmlDocument - Normal format");
Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds);
Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds);
//xml parsing using XDocument - Normal format
objSW.Reset();
objSW.Start();
xNDoc = XDocument.Parse(xmlNDoc.OuterXml);
foreach (XElement e in xNDoc.Root.Elements())
{
foreach (XElement c in e.Elements())
{
string strName = c.Name.ToString();
string strValue = c.Value;
//Console.WriteLine(strName+"-"+strValue);
}
}
xNDoc = null;
objSW.Stop();
Console.WriteLine("XML parsing using XDocument - Normal format");
Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds);
Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds);
//xml parsing using XmlReader - Normal format
objSW.Reset();
objSW.Start();
using (xmlNReader = XmlReader.Create(strNormalFilePath))
{
while (xmlNReader.Read())
{
switch (xmlNReader.NodeType)
{
case XmlNodeType.Element:
{
switch (xmlNReader.Name)
{
case "Name":
case "Zip":
{
string strName = xmlNReader.Name;
xmlNReader.Read();
string strValue = xmlNReader.Value;
//Console.WriteLine(strName +
// "-" + strValue);
break;
}
case "Address1":
case "Address2":
case "Phone":
{
string strName = xmlNReader.Name;
xmlNReader.Read();
string strValue = xmlNReader.Value;
//Console.WriteLine(strName +
// "-" + strValue);
break;
}
}
break;
}
}
}
}
objSW.Stop();
Console.WriteLine("XML parsing using XmlReader - Normal format");
Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds);
Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
Output: XML parsing using XmlDocument - Normal format TimeElapsed (Stopwatch float):5232.8249ms TimeElapsed (Stopwatch rounded):5232ms XML parsing using XDocument - Normal format TimeElapsed (Stopwatch float):114.9677ms TimeElapsed (Stopwatch rounded):114ms XML parsing using XmlReader - Normal format TimeElapsed (Stopwatch float):42.7921ms TimeElapsed (Stopwatch rounded):42ms
Note: XmlDocument can be considered, if forward and backward reference is unavoidable during the parsing of xml document.
Rule: Use XmlReader for parsing xml document.
C# XPATH vs LINQ
Create an xml file as show below, but with 10000 contact elements called as “address_xw_n.xml” and make it available in D driver (d:\).
<AddressBook>
<Contact>
<Name>Person1</Name>
<Address1><![CDATA[Number101]]></Address1>
<Address2><![CDATA[Address501]]></Address2>
<Zip>50001</Zip>
<Phone><![CDATA[999991]]></Phone>
</Contact>
</AddressBook>
The below code demonstrate the time take to query the above created xml file using xpath and LINQ for the Phone element value “999999996“.using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Diagnostics;
using System.Xml.Linq;
namespace QueryXML
{
class Program
{
static void Main(string[] args)
{
Stopwatch objSW = null;
String strNormalFilePath;
strNormalFilePath = @"d:\address_xw_n.xml";
XmlDocument xmlNDoc;
xmlNDoc = new XmlDocument();
XDocument xNDoc;
objSW = new Stopwatch();
objSW.Reset();
objSW.Start();
//load the xml
xmlNDoc.Load(strNormalFilePath);
//xml query using xpath - Normal format
objSW.Reset();
objSW.Start();
XmlNode xmlQNNode = xmlNDoc.SelectSingleNode(
"/AddressBook/Contact/Phone[contains(text(),'999999996')]");
//Console.WriteLine(xmlQNNode.Name +
// "-" + xmlQNNode.ChildNodes[0].Value);
objSW.Stop();
Console.WriteLine("XML query using xpath - Normal format");
Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds);
Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds);
//xml query using LINQ - Normal format
//Let us not include the conversion of XmlDocument to XDocument
xNDoc = XDocument.Parse(xmlNDoc.OuterXml);
objSW.Reset();
objSW.Start();
var linqNQuery = from p in
xNDoc.Root.Elements("Contact").Elements("Phone")
where p.Value.Contains("999999996") select p;
/*foreach (XElement e in linqNQuery)
{
Console.WriteLine(e.Name+"-"+e.Value);
}*/
objSW.Stop();
Console.WriteLine("XML query using LINQ - Normal format");
Console.WriteLine("TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds);
Console.WriteLine("TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
Output: XML query using xpath - Normal format TimeElapsed (Stopwatch float):20.0936ms TimeElapsed (Stopwatch rounded):20ms XML query using LINQ - Normal format TimeElapsed (Stopwatch float):0.5082ms TimeElapsed (Stopwatch rounded):0ms
Rule: Use LINQ for querying xml content.
Wednesday, September 15, 2010
MySQL performance optimization
Rue: Use MEDIUMINT for storing user Id for intranet applications. You can store -8388608 to 8388607 signed values or 0 to 16777215 unsigned values.
Rue: You can consider using BIGINT UNSIGNED for storing user Id for internet based applications. You can store -9223372036854775808 to 9223372036854775807 signed values or 0 to 18446744073709551615 unsigned values.
Rue: Always use UNSIGNED numeric types unless you want to store negative numbers.
Rue: Always have an primary key id column which is one of the INT types and UNSIGNED.
Rue: Use ENUM type over VARCHAR for storing predefined string values, e.g. "inactive", "inactive", "male", "female" etc.
Rue: Use DATE type instead of DATETIME if you want to store only date.
Rue: Use VARCHAR for variable string values.
Rue: Use INT UNSIGNED for IP4 values.
Rue: Using NOT NULL saves 1 bit per column.
Rue: Use binary type to store md5 value with 16 bytes instead of 32 bytes of varchar type.
Rue: Consider fixed format table structure (no varchar, no blob or no text columns) for tables with more write operations.
Rue: Use LIMIT in your SELECT, UPDATE statements if you already know your are looking for only one match, e.g. while checking for username and password match.
Rue: Avoid SELECT * FROM and specify only the interested column names.
Rue: Use index only when needed, indexes are good for reading and bad for storing data quickly. Identify the proportion of read and write operation on every single table in your database.
Rue: Consider storing image data outside the database and store only the reference of the image in the database. You can reconsider this approach if you want to support replication of servers.
For more information please refer the following sites,http://dev.mysql.com/doc/refman/5.1/en/storage-engines.html
http://dev.mysql.com/doc/refman/5.1/en/optimization.html
http://www.slideshare.net/ronaldbradford/top-20-design-tips-for-mysql-data-architects-presentation
http://net.tutsplus.com/tutorials/other/top-20-mysql-best-practices/
Thursday, September 2, 2010
Improving the performance of web page | web application
If you are using Google Chrome then try Speed Tracer. If you are using Firefox then try
Page Speed.
You can also try online performance testing at webpagetest.
Let us see some simple rules to improve the performance of the web page,
Rule: Remove broken links (<a>).
Rule: Combine the external javascript links into 2 or three links to reduce the HTTP request.
Rule: Use inline javascript for fewer lines of script.
Rule: To make the home page load faster have only the scripts needed for the home page either as external link or inline.
You can also try to defer the loading of javascript.Rule: Try to defer the loading of javascript if possible.
Refer Browserscope for knowing the we browsers parallel loading capabilities of javascripts and other resources.Rule: Combine the external css into 2 or three files to reduce the HTTP request.
Rule: Use inline css for fewer lines of css.
Rule: To make the home page load faster have only the css needed for the home page either as external link or inline.
Rule: Compress and compact the html, css and javascript resources to reduce the number of bytes sent over the network.
Rule: Minimize DNS lookups to reduce the resolution requests. Use URL paths e.g. host your site on www.xyz.com/abc instead of abc.xyz.com. Serve the startup javascript and other resources from the same host.
Rule: Minimize redirection from one URL to another.
Rule: Use image compressor. This would reduce number of bytes sent over the network.
Rule: Make the styles (css) load before the scripts (javascript). This would enables better parallelization of downloads and speeds up browser rendering.
Refer Browserscope for knowing the we browsers parallel loading capabilities of javascripts and other resources.Rule: Parallelize the loading of resources.
Rule: Have the inline style and external style sheets in the "head" section.
Rule: Remove the unused css, javascript and html. This would eliminate unwanted bytes sent over the network.
Rule: Specify the size (height and width) of the image in the img tag.
For more details please refer Web Performance Best Practices and Let's make the web faster.
Now try applying the above rules to your web page and measure the performance.
Watch the below two videos for more on improving web page | web application performance.
Speed Tracer
Page Speed
Wednesday, August 18, 2010
Naming convention
The name of the folder, project, file, namespace, class, and function have to be meaningful. The variable (data members, local members or function arguments) must have the following information as part of their name
- modifier (private, public, etc.) and scope of the variable (member to the class (data member), member to the function (local variable), constant, static)
- datatype of the variable
- meaningful name
C# 2.0 example- Below is the Employee.cs file.
using System;
using System.Collections.Generic;
using System.Text;
namespace Sample_v2
{
class Employee
{
private string m_strName=String.Empty;
private int m_iEmpId=-1;
public Employee(string strName, int iEmpId)
{
m_strName = strName;
m_iEmpId = iEmpId;
}
public string Name
{
get
{
return m_strName;
}
private set { m_strName = value; }
}
public int EmpId
{
get
{
return m_iEmpId;
}
private set { m_iEmpId = value; }
}
//should not allow empty string or null string
public bool SetNameWithValidation(string strName)
{
bool blnStatus = false;
if (strName != string.Empty && strName != null)
{
m_strName = strName;
blnStatus = true;
}
return blnStatus;
}
public override string ToString()
{
return string.Format("Name:{0},EmpId:{1}",
m_strName, m_iEmpId);
}
}
}
C# 3.0 example- Below is the Employee.cs file.
using System;
using System.Collections.Generic;
using System.Text;
namespace Sample_v3
{
class Employee
{
public string Name { get; set; }
public int EmpId { get; set; }
public Employee()
{
}
//should not allow empty string or null string
public bool SetName(string strName)
{
bool blnStatus = false;
if (strName != string.Empty && strName != null)
{
Name = strName;
blnStatus = true;
}
return blnStatus;
}
public override string ToString()
{
return string.Format("Name:{0},EmpId:{1}",
Name,
EmpId);
}
}
}
Note: Alternatively you can follow the naming standard followed by the respective technology (.Net, J2SE, JavasScript, etc). Intellisense feature of the respective integrated development environment (IDE) could help you on this.
Rule: Follow consistent naming convention.
Rule: If you not clear on the final name of the product, module, etc consider using a meaning less code name (e.g. "code123").
Thursday, June 17, 2010
Optimizing C# 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):319msStep #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):4msStep #5 - Analyze the advantages and disadvantages of different approaches and decide the best approach.
Write the code → Measure → Optimize the code → Measure
C# measurement tools
- Time taken to complete some operation
- Memory usage of the application
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace StructMemoryUsage
{
struct EmployeeStruct
{
public string Name;
public int EmpId;
}
class Program
{
static void Main(string[] args)
{
long lngGCMemStart = 0;
long lngGCMemEnd = 0;
long lngProcessMemStart = 0;
long lngProcessMemEnd = 0;
//employee as struct
EmployeeStruct[] objEmployee;
int iCount;
iCount = 100000;
Process objProcess = Process.GetCurrentProcess();
//Measure starting point memory
lngGCMemStart = System.GC.GetTotalMemory(true);
lngProcessMemStart = objProcess.PrivateMemorySize64;
objEmployee = new EmployeeStruct[iCount];
//Measure memory after allocating
lngGCMemEnd = System.GC.GetTotalMemory(true);
lngProcessMemEnd = objProcess.PrivateMemorySize64;
for (int i = 0; i < iCount; i++)
{
objEmployee[i].Name = "Emp" + i.ToString();
objEmployee[i].EmpId = i;
}
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:802380 (bytes) Process Memory Use:0 (bytes) GC Memory Start:126592 (bytes) Process Memory Start:8937472 (bytes) GC Memory End:928972 (bytes) Process Memory End:8937472 (bytes)
Note: Alternatively use perfmon->Add Counters->.NET CLR Memory counters can be used for viewing the memory consumption of selected .NET process.
Below is the code for measuring the time.using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace MeasuringTime
{
class Program
{
static void Main(string[] args)
{
long lngStartTime = 0;
long lngEndTime = 0;
DateTime dtStartTime;
TimeSpan tsTimeDiff;
long lngSWFrequency=0;
Stopwatch objSW;// Only from .Net 2.0
//If the computer is kept running continuously
//for 49 days then the Environment.TickCount would
//start counting backwards
//So the most significant bit is extracted
lngStartTime = Environment.TickCount
& Int32.MaxValue;
System.Threading.Thread.Sleep(5000);
lngEndTime = Environment.TickCount
& Int32.MaxValue;
Console.WriteLine("TimeElapsed (TickCount):{0}ms",
lngEndTime - lngStartTime);
//Resolution of DateTime.Now is 10+ milliseconds
dtStartTime = DateTime.Now;
System.Threading.Thread.Sleep(5000);
tsTimeDiff = DateTime.Now - dtStartTime;
Console.WriteLine("TimeElapsed (DateTime):{0}ms",
tsTimeDiff.TotalMilliseconds);
//Stopwatch uses the hardware timer if it
//highresolution timer or else uses the DateTime.Now
//To check the availability of the high
//resolution timer use IsHighResolution property
if (true == Stopwatch.IsHighResolution)
{
Console.WriteLine(
"HighResolutionTimer is present"
);
}
else
{
Console.WriteLine(
"HighResolutionTimer is absent"
);
}
lngSWFrequency = Stopwatch.Frequency;
Console.WriteLine(
"Timer frequency in ticks/second:{0}",
lngSWFrequency);
objSW = Stopwatch.StartNew();
System.Threading.Thread.Sleep(5000);
objSW.Stop();
Console.WriteLine(
"TimeElapsed (Stopwatch float):{0}ms",
objSW.Elapsed.TotalMilliseconds
);
Console.WriteLine(
"TimeElapsed (Stopwatch rounded):{0}ms",
objSW.ElapsedMilliseconds
);
Console.ReadLine();
}
}
}
Output: TimeElapsed (TickCount):4992ms TimeElapsed (DateTime):5002ms HighResolutionTimer is present Timer frequency in ticks/second:14318180 TimeElapsed (Stopwatch float):5000.7317ms TimeElapsed (Stopwatch rounded):5000ms
Note: Profiling tools can be used for the measuring time and memory leak.
Compact xml
For example if we want to store address in an xml file then it would look some thing as below
<AddressBook>
<Contact>
<Name>Person1</Name>
<Address1><![CDATA[Number101]]></Address1>
<Address2><![CDATA[Address501]]></Address2>
<Zip>50001</Zip>
<Phone><![CDATA[999991]]></Phone>
</Contact>
</AddressBook>
If we use the xml format to store the data and exchange data in an internal or intranet application then consider changing the format compact as below <AB>
<C>
<N>Person1</N>
<A1><![CDATA[Number101]]></A1>
<A2><![CDATA[Address501]]></A2>
<Z>50001</Z>
<P><![CDATA[999991]]></P>
</C>
</AB>
Keeping the tag name shot reduces the overall xml file size. Also have bandwidth advantage if transfered across the network.Create an xml file with normal tag names with 10000 Contact nodes. Create another xml file with short tag names with 10000 C nodes.
Output: The size of the xml file with normal tag name is approximately 2.08 MB. The size of the xml file with short tag name is approximately 1.57 MB.Below is an example of xml file with short attribute name.
<AB>
<C N="Person1" Z="50001">
<A1><![CDATA[Number101]]></A1>
<A2><![CDATA[Address501]]></A2>
<P><![CDATA[999991]]></P>
</C>
</AB>
Rule: Consider short names for xml tags (element name or node name) and attributes.
Return variable
C# example- In the below example blnStatus is initialized to false.
//should not allow empty string or null string
public bool SetName(string strName)
{
bool blnStatus = false;
if (strName != string.Empty && strName != null)
{
Name = strName;
blnStatus = true;
}
return blnStatus;
}
Rule: Initialize the return variable to failure status value.
Rule: Make sure return variable is set to success status value at the right place.
Single return
C# example- Code with multiple return statement.
//should not allow empty string or null string
public bool SetName(string strName)
{
if (strName != string.Empty && strName != null)
{
m_strName = strName;
return true;
}
return false;
}
C# example- Code with single return statement.
//should not allow empty string or null string
public bool SetName(string strName)
{
bool blnStatus = false;
if (strName != string.Empty && strName != null)
{
m_strName = strName;
blnStatus = true;
}
return blnStatus;
}
Rule: Have single return statement.
Thread safety
C#- Make sure the mutex, monito or semaphore, if used, are released properly.
Note: A semaphore with a capacity of one is similar to mutex or lock. Semaphore is thread-agnostic because it has no owner. Any thread can call release a semaphore, but with lock and mutex, only the thread that acquired the resource can release it.
C# example- Below Logger class covers using lock and monitor, releasing monitor and protecting only the resource being shared across threads.
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;
}
//make sure the StreamWriter is closed
//make sure thr Monitor is exit
public bool WriteToFile_Local(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;
}
//exception has to be handled by the caller
//can not be unit tested using UnitTestingTool class
public void WriteToFile_Local_Using(string strMessage)
{
StreamWriter objStreamWriter = null;
if (strMessage != string.Empty && strMessage != null)
{
//private member variable
lock (m_objLocker)
{
using (objStreamWriter =
new StreamWriter(FilePath, true))
{
//logs only the time and not the date
objStreamWriter.WriteLine(
DateTime.Now.TimeOfDay + " - " +
strMessage
);
}
}
}
}
}
Rule: Write thread safe code if your application is multi threaded.
Rule: Protecting only the resource being shared across threads or processes.
Rule: Releasing the mutex, monitor or semaphore if used.
Release resources
- closing the database connection
- closing the file handle
- closing the communication port (RS232, Socket, USB, UDP, etc)
the finally block to release the resources. In some case the resource may
have to be released in catch block. Otherwise the “Using”
(gets compiled to try/finally block) block can be used.
C# example- Below code has StreamWriter and try/catch/finally block.
//make sure the StreamWriter is closed
//make sure thr Monitor is exit
public bool WriteToFile_Local(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;
}
C# example- Below code has StreamWriter and Using block.
//exception has to be handled by the caller
//can not be unit tested using UnitTestingTool class
public void WriteToFile_Local_Using(string strMessage)
{
StreamWriter objStreamWriter = null;
if (strMessage != string.Empty && strMessage != null)
{
//private member variable
lock (m_objLocker)
{
using (objStreamWriter =
new StreamWriter(FilePath, true))
{
//logs only the time and not the date
objStreamWriter.WriteLine(
DateTime.Now.TimeOfDay + " - " + strMessage
);
}
}
}
}
Note: When you are done with the reference variable it need
not be set to null, because once a variable fall out of scope,
it is popped from the stack and the reference is removed.
Rule: Close the database connection.
Rule: Close the communication port.
Rule: Close the file handle.