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();
colValue = dataSet.Tables[0].Rows[0][1].ToString();
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];
String colValue = dr[1].ToString();
colValue = dr[1].ToString();
colValue = dr[2].ToString();
}
}
The IL code for the above functions, I've added proper comments(green) to elaborate it:
.method public hidebysig instance void FillTableValuesA() cil managed
{
.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
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: ldc.i4.1
IL_0030: callvirt instance object [System.Data]System.Data.DataRow::get_Item(int32)
IL_0035: callvirt instance string [mscorlib]System.Object::ToString()
IL_003a: stloc.1
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
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
IL_0081: ret
}
//Function ends here
//Function FillTableValuesB starts here
.method public hidebysig instance void FillTableValuesB() cil managed
{
.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
IL_0030: ldloc.1
IL_0031: ldc.i4.1
IL_0032: callvirt instance object [System.Data]System.Data.DataRow::get_Item(int32)
IL_0037: callvirt instance string [mscorlib]System.Object::ToString()
IL_003c: stloc.2
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
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
IL_0057: ret
}
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.