Jul 05 2006

Performance exceptions vs error codes

Posted by admin under .NET

Another real life experience. Once a long time ago I wrote a simple Windows GUI application where users could enter float values.

 

Some users preferred using "," as delimiter and some other ".", i.e my application needed
to convert strings like "122,12" and "122.12" to a double value. Also blanks - which should be treated as 0.0.

 

As I said it was a long time ago, and I used try...catch to see if I would succeed in getting a double value out of it.

 


public static double MyToDouble(string sTxt)
{
	sTxt = sTxt.Trim();
	if ( sTxt == "" )
		return 0;

	double dRet = -1000;
	try
	{
		dRet = Convert.ToDouble(sTxt);
	}
	catch
	{
	}
	if ( dRet == -1000 )
	{
		try
		{
			string sTemp = Convert.ToString(sTxt);
			sTemp = sTemp.Replace(",",".");
			dRet = Convert.ToDouble(sTemp);
		}
		catch
		{
		}
	}
	if ( dRet == -1000 )
	{
		try
		{
			string sTemp = Convert.ToString(sTxt);
			sTemp = sTemp.Replace(".",",");
			dRet = Convert.ToDouble(sTemp);
		}
		catch
		{
		}
	}
	return dRet;
}
 

 It worked (and still work) perfectly in that specific application. However yesterday I needed to handle a pretty large batch file (133MB) generated from another system and basically import the records into my own SQL Server database. Also, some fields used "," as delimiter and some other fields "." - don't ask me why, but then I remembered my nice little MyToDouble routine and throwed it into use. Guess what - the routine (inserting 500000 records into SQL Server) took almost a day to run... Also, some things went wrong and I needed to rerun it - of course...

 

So, I started analyzing what was the problem. Throughput to the database was ok - and finally I found out that the bad guy was MyToDouble - which was called around 8 times for each record (500000 remember...) and therefore throwing up to 10 millions of exceptions... I change it to use TryParse - and viola. The whole batch import ran in just a few minutes... To further illustrate the problems with exceptions - consider this little program:

 

 

void VerifyDataAndThrowIfError(string sVal)
{
	if ( sVal != "A" )
		throw new FormatException("Wrong");
}

bool VerifyDataAndReturnBool(string sVal)
{
	if ( sVal != "A" )
		return false;
	return true;
}


private void button1_Click(object sender, System.EventArgs e)
{
	System.DateTime dt = DateTime.Now;
	int nError = 0;
	for(int i = 0; i < 500000; i++ )
	{
		if ( VerifyDataAndReturnBool("B") == false ) 
			nError++;
	}
	TimeSpan oSpan = DateTime.Now - dt;
	label1.Text = oSpan.TotalMilliseconds.ToString();
}


private void button2_Click(object sender, System.EventArgs e)
{
	System.DateTime dt = DateTime.Now;
	int nError = 0;
	for(int i = 0; i < 500000; i++ )
	{
		try
		{
			VerifyDataAndThrowIfError("B");
		}
		catch
		{
			nError++;
		}
	}
	TimeSpan oSpan = DateTime.Now - dt;
	label1.Text = oSpan.TotalMilliseconds.ToString();
		
}

As you can see we have two types of verifications. One throwing an error when validation fails - the other just return an error code (or actually a bool). Running VerifyDataAndReturnBool 500000 times with failing validation took 31.56 milliseconds. Running VerifyDataAndReturnBool 500000 times with failing validation took - well I don't know. After TWO MINUTES I interrupted the program and it had only managed a little than more than 100000 iterations at that time. So, really think about this. If your code is being called many times - using error codes instead of exception handling is really preferred.

ASPCode.net recommends