Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Coding Tip for Performance Improvement .Net

4.53/5 (20 votes)
17 May 2010CPOL1 min read 23.4K  
I always find this thing while doing code reviews and that is a performance issue. The above point I haven’t found anywhere as a performance issue, so I thought to share it.Scenario: There is a function in which one need to extract 10-20 values from an object for example a column value from...
I always find this thing while doing code reviews and that is a performance issue. The above point I haven’t found anywhere as a performance issue, so I thought to share it.

Scenario: There is a function in which one need to extract 10-20 values from an object for example a column value from Dataset. Mostly it was written as:

String colValue = dataSet.Tables[0].Rows[0][0]
colValue = dataSet.Tables[0].Rows[0][1]
.
colValue = dataSet.Tables[0].Rows[0][20]


So what is wrong with the above code?
Let me ask you a question before I answer this – How does a CLR executes your c# code? Ahhhh… common you will say – the compiler converts it to Intermediate language and then CLR will execute it with JIT. Great you know it all, but do you check IL generated for you code?. (I’m not an exception here ;))

Try it yourself – write the above code in an library and open it (dll) with ILDASM and navigate the function/method and double click it to see the IL code generated for that function.

All right I’ll suggest to write above code like this

DataRow dr = DataSet.Tables[0].Rows[0];
String colValue = dr[0]
colValue = dr[1]
.
.
colValue = dr[20]


Check the IL code generated for the following code. I’ve attached the c# code and its IL version:

public class ILExample
    {
        public void FillTableValuesA()
        {
            DataSet dataSet = new DataSet();
            dataSet.Tables.Add(new DataTable());
            String colValue = dataSet.Tables[0].Rows[0][1].ToString(); //There are 11 lines of IL for this single line
            colValue = dataSet.Tables[0].Rows[0][1].ToString();   //hence for 3 calls there are 33 lines
            colValue = dataSet.Tables[0].Rows[0][2].ToString();
        }
        public void FillTableValuesB()
        {
            DataSet dataSet = new DataSet();
            dataSet.Tables.Add(new DataTable());
            DataRow dr = dataSet.Tables[0].Rows[0]; //Here are few additional lines but worthy
            String colValue = dr[1].ToString();//There are 4 lines of IL for this single line
            colValue = dr[1].ToString();  //hence for 3 calls there are 12 lines
            colValue = dr[2].ToString();
        }
    }

The IL code for the above functions, I've added proper comments(green) to elaborate it:
MSIL
.method public hidebysig instance void  FillTableValuesA() cil managed
{
  // Code size       130 (0x82)
  .maxstack  2
  .locals init ([0] class [System.Data]System.Data.DataSet dataSet,
           [1] string colValue)
  IL_0000:  nop
  IL_0001:  newobj     instance void [System.Data]System.Data.DataSet::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  callvirt   instance class [System.Data]System.Data.DataTableCollection [System.Data]System.Data.DataSet::get_Tables()
  IL_000d:  newobj     instance void [System.Data]System.Data.DataTable::.ctor()
  IL_0012:  callvirt   instance void [System.Data]System.Data.DataTableCollection::Add(class [System.Data]System.Data.DataTable)
  IL_0017:  nop
//This code is for String colValue = dataSet.Tables[0].Rows[0][1].ToString();
//First it loads DataSet Object
  IL_0018:  ldloc.0
// Call getTables() function to get .Tables collection
  IL_0019:  callvirt   instance class [System.Data]System.Data.DataTableCollection [System.Data]System.Data.DataSet::get_Tables()
  IL_001e:  ldc.i4.0
// Call get_Item() function to get 0th Table from Tables collection
  IL_001f:  callvirt   instance class [System.Data]System.Data.DataTable [System.Data]System.Data.DataTableCollection::get_Item(int32)
// Call get_Rows() function to get .Rows collection from 0th Table
  IL_0024:  callvirt   instance class [System.Data]System.Data.DataRowCollection [System.Data]System.Data.DataTable::get_Rows()
  IL_0029:  ldc.i4.0
// Call get_Item() function to get 0th Row from .Rows collection
  IL_002a:  callvirt   instance class [System.Data]System.Data.DataRow [System.Data]System.Data.DataRowCollection::get_Item(int32)
  IL_002f:  ldc.i4.1
// Call get_Item() function to get 1st column from 0th Row
  IL_0030:  callvirt   instance object [System.Data]System.Data.DataRow::get_Item(int32)
// Convert the column’s value to string
  IL_0035:  callvirt   instance string [mscorlib]System.Object::ToString()
// Store the current value on Stack (i.e. string value of the column) to 1st local variable 
//(i.e. colValue; check .locals init function at the beginning)
  IL_003a:  stloc.1
//Code ends here

//This code is for colValue = dataSet.Tables[0].Rows[0][1].ToString();
  IL_003b:  ldloc.0
  IL_003c:  callvirt   instance class [System.Data]System.Data.DataTableCollection [System.Data]System.Data.DataSet::get_Tables()
  IL_0041:  ldc.i4.0
  IL_0042:  callvirt   instance class [System.Data]System.Data.DataTable [System.Data]System.Data.DataTableCollection::get_Item(int32)
  IL_0047:  callvirt   instance class [System.Data]System.Data.DataRowCollection [System.Data]System.Data.DataTable::get_Rows()
  IL_004c:  ldc.i4.0
  IL_004d:  callvirt   instance class [System.Data]System.Data.DataRow [System.Data]System.Data.DataRowCollection::get_Item(int32)
  IL_0052:  ldc.i4.1
  IL_0053:  callvirt   instance object [System.Data]System.Data.DataRow::get_Item(int32)
  IL_0058:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_005d:  stloc.1
//Code ends here

//This code is for colValue = dataSet.Tables[0].Rows[0][2].ToString();
  IL_005e:  ldloc.0
  IL_005f:  callvirt   instance class [System.Data]System.Data.DataTableCollection [System.Data]System.Data.DataSet::get_Tables()
  IL_0064:  ldc.i4.0
  IL_0065:  callvirt   instance class [System.Data]System.Data.DataTable [System.Data]System.Data.DataTableCollection::get_Item(int32)
  IL_006a:  callvirt   instance class [System.Data]System.Data.DataRowCollection [System.Data]System.Data.DataTable::get_Rows()
  IL_006f:  ldc.i4.0
  IL_0070:  callvirt   instance class [System.Data]System.Data.DataRow [System.Data]System.Data.DataRowCollection::get_Item(int32)
  IL_0075:  ldc.i4.2
  IL_0076:  callvirt   instance object [System.Data]System.Data.DataRow::get_Item(int32)
  IL_007b:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_0080:  stloc.1
//Code ends here

  IL_0081:  ret
} // end of method ILExample::FillTableValuesA

//Function ends here

//Function FillTableValuesB starts here
MSIL
.method public hidebysig instance void  FillTableValuesB() cil managed
{
  // Code size       88 (0x58)
  .maxstack  2
  .locals init ([0] class [System.Data]System.Data.DataSet dataSet,
           [1] class [System.Data]System.Data.DataRow dr,
           [2] string colValue)
  IL_0000:  nop
  IL_0001:  newobj     instance void [System.Data]System.Data.DataSet::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  callvirt   instance class [System.Data]System.Data.DataTableCollection [System.Data]System.Data.DataSet::get_Tables()
  IL_000d:  newobj     instance void [System.Data]System.Data.DataTable::.ctor()
  IL_0012:  callvirt   instance void [System.Data]System.Data.DataTableCollection::Add(class [System.Data]System.Data.DataTable)
  IL_0017:  nop
  IL_0018:  ldloc.0
  IL_0019:  callvirt   instance class [System.Data]System.Data.DataTableCollection [System.Data]System.Data.DataSet::get_Tables()
  IL_001e:  ldc.i4.0
  IL_001f:  callvirt   instance class [System.Data]System.Data.DataTable [System.Data]System.Data.DataTableCollection::get_Item(int32)
  IL_0024:  callvirt   instance class [System.Data]System.Data.DataRowCollection [System.Data]System.Data.DataTable::get_Rows()
  IL_0029:  ldc.i4.0
  IL_002a:  callvirt   instance class [System.Data]System.Data.DataRow [System.Data]System.Data.DataRowCollection::get_Item(int32)
  IL_002f:  stloc.1
//This code is for String colValue = dr[1].ToString();
// Load first local variable that is dr
  IL_0030:  ldloc.1
  IL_0031:  ldc.i4.1
// Get first column
  IL_0032:  callvirt   instance object [System.Data]System.Data.DataRow::get_Item(int32)
// Convert the column’s value to string
  IL_0037:  callvirt   instance string [mscorlib]System.Object::ToString()
// Store the current value on Stack (i.e. string value of the column) to 2nd local variable 
//(i.e. colValue; check .locals init function at the beginning)
  IL_003c:  stloc.2
//Code ends here

//This code is for colValue = dr[1].ToString();
  IL_003d:  ldloc.1
  IL_003e:  ldc.i4.1
  IL_003f:  callvirt   instance object [System.Data]System.Data.DataRow::get_Item(int32)
  IL_0044:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_0049:  stloc.2
//Code ends here

//This code is for colValue = dr[2].ToString();
  IL_004a:  ldloc.1
  IL_004b:  ldc.i4.2
  IL_004c:  callvirt   instance object [System.Data]System.Data.DataRow::get_Item(int32)
  IL_0051:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_0056:  stloc.2
//Code ends here

  IL_0057:  ret
} // end of method ILExample::FillTableValuesB


The collections make above code more expensive because to get one item it has to search whole collection(with indexes its easy though). And the second function makes the code more legible.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)