Re: Reflection in 1.1 and 2.0



I'd suggest you take advantage of Lightweight Code Generation (LCG).

I knocked a v2.0 sample together using your example.

NO LCG:
Runtime version = 2.0.50727.42
Iterations = 1,000,000
00:00:04.8898738

USING LCG:
Runtime version = 2.0.50727.42
Iterations = 1,000,000
00:00:00.0937632

- - - - - COPY BELOW - - - - - -

#define LCG
// remove this define to use the old PropertyInfo way.

using System;
using System.Reflection;
using System.Reflection.Emit;

public class Person
{
private string _firstName;
private string _lastName;
private int _age;

public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}

public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}

public int Age
{
get { return _age; }
set { _age = value; }
}
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Runtime version = {0}", Environment.Version);

int iterations = args.Length > 0 ? int.Parse(args[0]) : 1000000;
Console.WriteLine("Iterations = {0}", iterations.ToString("N0"));

Person person = new Person();
person.FirstName = "Hello";


PropertyInfo fn = typeof(Person).GetProperty("FirstName");
PropertyInfo ln = typeof(Person).GetProperty("LastName");
PropertyInfo age = typeof(Person).GetProperty("Age");

DateTime start = DateTime.Now;

#if LCG
FastSetterDelegate<string> fns = GenerateFastSetter<string>(fn);
FastSetterDelegate<string> lns = GenerateFastSetter<string>(ln);
FastSetterDelegate<int> ages = GenerateFastSetter<int>(age);

for (int i = 0; i < iterations; i++)
{
fns(person, "John");
lns(person, "Doe");
ages(person, 42);
}
#else
for (int i = 0; i < iterations; i++)
{
fn.SetValue(person, "John", null);
ln.SetValue(person, "Doe", null);
age.SetValue(person, 42, null);
}
#endif
Console.WriteLine(DateTime.Now - start);
}

static FastSetterDelegate<T> GenerateFastSetter<T>(PropertyInfo pi)
{
DynamicMethod dm = new DynamicMethod(pi.Name, null, new Type[] { typeof(object), typeof(T) }, typeof(Program).Module);

ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Callvirt, pi.GetSetMethod(), null);
il.Emit(OpCodes.Ret);

return (FastSetterDelegate<T>)dm.CreateDelegate(typeof(FastSetterDelegate<T>));
}

delegate void FastSetterDelegate<T>(object obj, T value);
}


Eric wrote:
I have here a little sample which does reflection on a class.
I striked me that 1.1 is nearly two times faster.
Could me somebody give a hint why?

For the sample: Compile it via "csc.exe /optimize+ Program.cs" with the two compiler for 1.1 and 2.0.

Thanks
Eric

namespace ComponentProfiling
{
using System;
using System.ComponentModel;
using System.Reflection;

public sealed class Person
{
private string _firstName;
private string _lastName;
private int _age;

public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}

public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}

public int Age
{
get { return _age; }
set { _age = value; }
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Runtime version = {0}", Environment.Version);

int iterations = args.Length > 0 ? int.Parse(args[0]) : 1000000;
Console.WriteLine("Iterations = {0}", iterations.ToString("N0"));

Person person = new Person();

PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(Person));
string test1 = typeof(TypeDescriptor).Name;
TimeSpan span1 = Measure(iterations, person,
properties["FirstName"],
properties["LastName"],
properties["Age"]);

Console.WriteLine("{0} = {1}", test1, span1);
string test2 = typeof(MyPropertyDescriptor).Name;
TimeSpan span2 = Measure(iterations, person,
new MyPropertyDescriptor(typeof(Person).GetProperty("FirstName")),
new MyPropertyDescriptor(typeof(Person).GetProperty("LastName")),
new MyPropertyDescriptor(typeof(Person).GetProperty("Age")));

Console.WriteLine("{0} = {1}", test2, span2);
Console.WriteLine("{0}/{1} = {2:P}", test1, test2, span1.TotalMilliseconds / span2.TotalMilliseconds);

Console.WriteLine("Press ENTER to end.");
Console.ReadLine();
}

static TimeSpan Measure(int iterations, Person person, params PropertyDescriptor[] properties)
{
DateTime start;
start = DateTime.Now;
for (int i = 0; i < iterations; i++)
{
properties[0].SetValue(person, "John");
properties[1].SetValue(person, "Doe");
properties[2].SetValue(person, 22); }
return DateTime.Now - start;
}

public sealed class MyPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyInfo _property;

public MyPropertyDescriptor(PropertyInfo property)
:
base(property.Name, null)
{
_property = property;
}

public override bool CanResetValue(object component)
{
return false;
}

public override object GetValue(object component)
{
return _property.GetValue(component, null);
}

public override void ResetValue(object component)
{
throw new NotSupportedException();
}

public override void SetValue(object component, object value)
{
_property.SetValue(component, value, null);
}

public override bool ShouldSerializeValue(object component)
{
return false;
}

public override Type ComponentType
{
get { return _property.ReflectedType; }
}

public override bool IsReadOnly
{
get { return !_property.CanWrite; }
}

public override Type PropertyType
{
get { return _property.PropertyType; }
}
}
}
}

.



Relevant Pages

  • Re: Reflection in 1.1 and 2.0 - generic, fast copy delegate?
    ... private string _firstName; ... private int _age; ... I'm well aware of LCG, and in fact, I'm not even sure why you need it in 2.0. ...
    (microsoft.public.dotnet.framework.clr)
  • Re: Reflection in 1.1 and 2.0 - generic, fast copy delegate?
    ... Your delegate solution is is still significantly more efficient that reflection. ... Also you need permissions to emit code with the LCG version whereas using delegates does not appear to require permissions beyond those for public reflection and so you can have a fast solution even in low trust environments. ... private string _firstName; ...
    (microsoft.public.dotnet.framework.clr)
  • Re: Reflection in 1.1 and 2.0
    ... You can get the same results without LCG using purely delegates. ... have you tried making Person private? ... private string _firstName; ... public override bool CanResetValue ...
    (microsoft.public.dotnet.framework.clr)
  • Re: Reflection in 1.1 and 2.0
    ... You can get the same results without LCG using purely delegates. ... have you tried making Person private? ... private string _firstName; ... public override bool CanResetValue ...
    (microsoft.public.dotnet.framework.clr)
  • Re: Reflection in 1.1 and 2.0 - generic, fast copy delegate?
    ... I had a scenario where I was constructing lots of objects, which are loaded dynamically, and Activator.CreateInstance is notoriously slow, so I created a delegate from a DynamicMethod to call the constructor directly. ... But once more, why use LCG? ... private string _firstName; ...
    (microsoft.public.dotnet.framework.clr)