Performance question regarding reflection

  • Thread starter Thread starter Invalidlastname
  • Start date Start date
I

Invalidlastname

Hi,
In our project, we used XSD.exe to create the classes, not dataset, which
represent the data entities from xsd files for passing among the tiers.
We want to set the default value for all numeric fields to be -1 in our data
entity classes. I know this can be done by setting the default value
attribute to -1, however, since we created the xsd directly from database
views and in early development stage the schemas changes happened very
often. Some custom set attributes can easily be lost. The solution we came
up is to use reflections to set the default value for all numeric fields,
shown below:

My question is how costly to use Reflection to instantiate an object and
examine every field, and probably assign the value to the field ?


// class DataEntActivator
public static object CreateInstance(System.Type aType)

{

object oDataEnt = Activator.CreateInstance(aType);

FieldInfo[] flds = aType.GetFields();

foreach (FieldInfo fldInfo in flds)

{

if (fldInfo.FieldType == typeof(short))

{

fldInfo.SetValue(oDataEnt , (short)-1);

}

else if (fldInfo.FieldType == typeof(int))

{

fldInfo.SetValue(oDataEnt , -1);

}

}


return oDataEnt ;

}





// calling program

MyDataEntity o =(MyDataEntity)
DataEntActivator.CreateInstance(typeof(MyDataEntity)) ;

// use o as a normal object
 
My question is how costly to use Reflection to instantiate an object and
examine every field, and probably assign the value to the field ?

Compared to doing it directly? Extremely. I'd suggest doing some
benchmarks. Here is a nice benchmarking framework:

http://www.yoda.arachsys.com/csharp/benchmark.html

-mike
MVP
// class DataEntActivator
public static object CreateInstance(System.Type aType)

{

object oDataEnt = Activator.CreateInstance(aType);

FieldInfo[] flds = aType.GetFields();

foreach (FieldInfo fldInfo in flds)

{

if (fldInfo.FieldType == typeof(short))

{

fldInfo.SetValue(oDataEnt , (short)-1);

}

else if (fldInfo.FieldType == typeof(int))

{

fldInfo.SetValue(oDataEnt , -1);

}

}


return oDataEnt ;

}





// calling program

MyDataEntity o =(MyDataEntity)
DataEntActivator.CreateInstance(typeof(MyDataEntity)) ;

// use o as a normal object
 
Hello

Definitely the use of reflection will affect performance. But I have some
suggestions to improve the performance of your application.

1 - passing -1, or (short)-1 to the setValue method will cause boxing to
occur because the are converted to object, then setValue will unbox them the
actually set the value of the field. Boxing will allocate heap memory for
every boxed value, and will take processor time to copy the value in heap.
Unboxing takes processor time to copy the value from the heap.
The unboxing is enevitable, but you can minimize the boxing by making a
static field of type object holding -1, and pass this object like this;
static object intM1 = -1;
static object shortM1 = (short)-1;

and in method fldInfo.SetValue(oDataEnt , intM1); or
fldInfo.SetValue(oDataEnt , shortM1);

This way boxing will occur 2 times only (for intM1 and shortM1) instead of
in every call to your method.

2 - If you try to call execute this line:
bool b = object.ReferenceEquals(aType.GetFields(), aType.GetFields());
you will find that b is false, which means that every time you call
GetFields a new array of FieldInfo objects is allocated. A way to solve this
is to make a static hashtable to cache the result of GetFields method in it.
So GetFields doesn't get executed every time., or better only cache the int
and short fields to save time wasted by testing the type of the field.
I recommend creating a static contructor and populate the hashtable with
arrays. So that the calls to GetFields are made just one time.

Below is how I do it (I have a similar application, but in mine i need to
set all DateTime fields to DateTime.MaxValue, and int values to int.MinValue
:) )

using System.Reflection;
using System.Collections;
class DataEntActivator
{
private static Hashtable FieldsTable;
private static object intM1;
private static object shortM1;

private class FieldsEntry
{
internal FieldInt[] intFlds;
internal FieldInt[] shortFlds;
ConstructorInfo cinfo;
FieldsEntry()
{
}
}

static DataEntActivator()
{
intM1 = -1;
shortM1 = (short)-1;
Assembly assembly = Assembly.Load("assemblyName");
Type[] allTypes = assembly.GetTypes();

FieldsTable = new Hashtable(allTypes.Length);
for(int i = 0; i < allTypes.Length; i++)
{
FieldInfo[] flds = allTypes.GetFields();
int intCount = 0, shortCount = 0;
for(int j = 0; j < flds.Length; j++)
{
if(flds[j].FieldType == typeof(int))
intCount++;
else if(flds[j].FieldType == typeof(short))
shortCount++;
}
FieldsEntry fe = new FieldEntry()
fe.intFlds = new FieldInfo[intCount];
fe.shortFlds = new FieldInfo[shortCount];
for(int j = 0; j < flds.Length; j++)
{
if(flds[j].FieldType == typeof(int))
fe.intFlds[--intCount] = flds[j];
else if(flds[j].FieldType == typeof(short))
fe.shortFlds[--shortCount] = flds[j];
}
fe.ConstructorInfo = allTypes.GetConstructor(Type.EmptyTypes);
fieldsTable.Add(allTypes, fe);
}
}

public static object CreateInstance(System.Type aType)
{
FieldsEntry fe = (FieldsEntry)FieldsTable[aType];
object oDataEnt = fe.ConstructorInfo.Info(null);
for(int i = 0; i < fe.intFlds.Length; i++)
{
fe.intFlds.SetValue(oDataEnt, intM1);
}
for(int i = 0; i < fe.shortFlds.Length; i++)
{
fe.shortFlds.SetValue(oDataEnt, shortM1);
}
return oDataEnt;
}
}

Invalidlastname said:
Hi,
In our project, we used XSD.exe to create the classes, not dataset, which
represent the data entities from xsd files for passing among the tiers.
We want to set the default value for all numeric fields to be -1 in our data
entity classes. I know this can be done by setting the default value
attribute to -1, however, since we created the xsd directly from database
views and in early development stage the schemas changes happened very
often. Some custom set attributes can easily be lost. The solution we came
up is to use reflections to set the default value for all numeric fields,
shown below:

My question is how costly to use Reflection to instantiate an object and
examine every field, and probably assign the value to the field ?


// class DataEntActivator
public static object CreateInstance(System.Type aType)

{

object oDataEnt = Activator.CreateInstance(aType);

FieldInfo[] flds = aType.GetFields();

foreach (FieldInfo fldInfo in flds)

{

if (fldInfo.FieldType == typeof(short))

{

fldInfo.SetValue(oDataEnt , (short)-1);

}

else if (fldInfo.FieldType == typeof(int))

{

fldInfo.SetValue(oDataEnt , -1);

}

}


return oDataEnt ;

}





// calling program

MyDataEntity o =(MyDataEntity)
DataEntActivator.CreateInstance(typeof(MyDataEntity)) ;

// use o as a normal object
 
Hi,

Thanks for your post. As you can see, using Reflection to modify the
fields' value (enumerate each field and assign value) will be slower than
instantiating an object with default value. I am not sure how sophiscated
the classes are and how frequently you will need to instantiate an object
with Reflection. I suggest that you can do some tests to compare the
performance of your application with and without using reflection.

Please feel free to let me know if you have any problems or concerns.

Have a nice day!

Regards,

HuangTM
Microsoft Online Partner Support
MCSE/MCSD

Get Secure! -- www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
Hello Sherif

Thanks for your informative posting! Have you done any benchmaring on the
initial approach suggested by the first posting and your optimized version?
 
Hello

Yes, I did, below is my benchmarking code. My version allocated 10000000
objects in 19.9375 seconds while the initial post version allocated them in
36.0781250. So my optimization runs in nearly 50% of the time.

Best regards
Sherif

static void Main(string[] args)
{
Type[] types = Assembly.Load("Moon").GetTypes();
int iterations = 10000000;
Random r = new Random();
DateTime t1, t2;
t1 = DateTime.Now;
for(int i = 0 ; i < iterations; i++)
{
DataEntActivator.CreateInstance(types[r.Next(0, types.Length)]);
}
t2 = DateTime.Now;
Console.WriteLine(t2 - t1);
t1 = DateTime.Now;
for(int i = 0 ; i < iterations; i++)
{
DataEntActivator2.CreateInstance(types[r.Next(0, types.Length)]);
}
t2 = DateTime.Now;
Console.WriteLine(t2 - t1);
}
 
Hi,
I really appreciate your excellent responses. I did implement the static
Hashtable internally for caching FieldInfo[] by all types (i ignored the
hashtable implementation for simplicity). I am glad to be able to verify
that hashtable approach is on the right track.

Here is another thought. Rather than caching the FieldInfo, I am wondering
if I can cache the default data entity object which has been initialized
with all numeric fields set to -1. These cached objects will serve as the
templates for new data entity object creations.

Since these data entity objects generated by xsd.exe and all inherit
"object" class, I cannot to find a good way to clone and memberwised copy
these template objects to the new objects. I think the XMLSerializer will do
the job, but the performance will be even worse than using Reflection. Any
suggestions?


Here are the benchmark results, using Benchmark.cs downloaded from
http://www.yoda.arachsys.com/csharp/benchmark.html, from my testing runs
which created 10,000 objects with 36 public fields (the fieldInfo and XML
string were cached)

UseXmlSerialization 00:00:08.7626000
UseReflection 00:00:00.3805472
UseReflectNoBoxingOp 00:00:00.3805472
UseDirect 00:00:00.0100144

100,000 objects
UseXmlSerialization 00:01:19.9549696
UseReflection 00:00:03.7554000
UseReflectNoBoxingOp 00:00:03.7253568
UseDirect 00:00:00.0400576


ILN

Sherif ElMetainy said:
Hello

Yes, I did, below is my benchmarking code. My version allocated 10000000
objects in 19.9375 seconds while the initial post version allocated them in
36.0781250. So my optimization runs in nearly 50% of the time.

Best regards
Sherif

static void Main(string[] args)
{
Type[] types = Assembly.Load("Moon").GetTypes();
int iterations = 10000000;
Random r = new Random();
DateTime t1, t2;
t1 = DateTime.Now;
for(int i = 0 ; i < iterations; i++)
{
DataEntActivator.CreateInstance(types[r.Next(0, types.Length)]);
}
t2 = DateTime.Now;
Console.WriteLine(t2 - t1);
t1 = DateTime.Now;
for(int i = 0 ; i < iterations; i++)
{
DataEntActivator2.CreateInstance(types[r.Next(0, types.Length)]);
}
t2 = DateTime.Now;
Console.WriteLine(t2 - t1);
}

Anders Borum said:
Hello Sherif

Thanks for your informative posting! Have you done any benchmaring on the
initial approach suggested by the first posting and your optimized version?
 
Hello

I tested the memberwiseclone approach (below is the code), it turned out to
be slower than the apporach you used in your initial post. I will test using
serialization with BinaryFormatter (which shour be faster then XML) and will
let you know the results.

Best regards,
Sherif

using System.Reflection;
using System.Collections;
using System;
class DataEntActivator3
{
private static Hashtable ObjectsTable;
private static object intM1;
private static object shortM1;
private static MethodInfo memberWiseClone;
static DataEntActivator3()
{
intM1 = -1;
shortM1 = (short)-1;
Assembly assembly = Assembly.Load("Moon");
Type[] allTypes = assembly.GetTypes();
memberWiseClone = typeof(object).GetMethod("MemberwiseClone",
BindingFlags.NonPublic | BindingFlags.Instance);
ObjectsTable = new Hashtable(allTypes.Length);
for(int i = 0; i < allTypes.Length; i++)
{
object obj =
allTypes.GetConstructor(Type.EmptyTypes).Invoke(null);
FieldInfo[] flds = allTypes.GetFields();
for(int j = 0; j < flds.Length; j++)
{
if(flds[j].FieldType == typeof(int))
flds[j].SetValue(obj, intM1);
else if(flds[j].FieldType == typeof(short))
flds[j].SetValue(obj, shortM1);
}
ObjectsTable.Add(allTypes, obj);
}
}

public static object CreateInstance(System.Type aType)
{
object oDataEnt = ObjectsTable[aType];
return memberWiseClone.Invoke(oDataEnt, null);
}
}
The problem with memberwise
Invalidlastname said:
Hi,
I really appreciate your excellent responses. I did implement the static
Hashtable internally for caching FieldInfo[] by all types (i ignored the
hashtable implementation for simplicity). I am glad to be able to verify
that hashtable approach is on the right track.

Here is another thought. Rather than caching the FieldInfo, I am wondering
if I can cache the default data entity object which has been initialized
with all numeric fields set to -1. These cached objects will serve as the
templates for new data entity object creations.

Since these data entity objects generated by xsd.exe and all inherit
"object" class, I cannot to find a good way to clone and memberwised copy
these template objects to the new objects. I think the XMLSerializer will do
the job, but the performance will be even worse than using Reflection. Any
suggestions?


Here are the benchmark results, using Benchmark.cs downloaded from
http://www.yoda.arachsys.com/csharp/benchmark.html, from my testing runs
which created 10,000 objects with 36 public fields (the fieldInfo and XML
string were cached)

UseXmlSerialization 00:00:08.7626000
UseReflection 00:00:00.3805472
UseReflectNoBoxingOp 00:00:00.3805472
UseDirect 00:00:00.0100144

100,000 objects
UseXmlSerialization 00:01:19.9549696
UseReflection 00:00:03.7554000
UseReflectNoBoxingOp 00:00:03.7253568
UseDirect 00:00:00.0400576


ILN

Sherif ElMetainy said:
Hello

Yes, I did, below is my benchmarking code. My version allocated 10000000
objects in 19.9375 seconds while the initial post version allocated them in
36.0781250. So my optimization runs in nearly 50% of the time.

Best regards
Sherif

static void Main(string[] args)
{
Type[] types = Assembly.Load("Moon").GetTypes();
int iterations = 10000000;
Random r = new Random();
DateTime t1, t2;
t1 = DateTime.Now;
for(int i = 0 ; i < iterations; i++)
{
DataEntActivator.CreateInstance(types[r.Next(0, types.Length)]);
}
t2 = DateTime.Now;
Console.WriteLine(t2 - t1);
t1 = DateTime.Now;
for(int i = 0 ; i < iterations; i++)
{
DataEntActivator2.CreateInstance(types[r.Next(0, types.Length)]);
}
t2 = DateTime.Now;
Console.WriteLine(t2 - t1);
}

Anders Borum said:
Hello Sherif

Thanks for your informative posting! Have you done any benchmaring on the
initial approach suggested by the first posting and your optimized version?
 
Back
Top