例如NHibernate,使用Emit读取/设置实体属性时,生成的DynamicMethod每次读取/设置全部的属性值,读取时返回一个object[],设置时提供一个包含各个属性值的object[]数组。在SELECT时使用这种方式一次设置好所有属性;在INSERT、UPDATE时则一次读出全部属性;在DELETE时则可以直接使用反射从实体上读取identity属性,因为从应用层面总体来看反射的性能还是比较好的。
Emit跟反射的性能测试对比如下:
Emit、Reflection代码:
//
A delegate for the dynamic method
public delegate object GetPropertyValueInvoker( object obj);
public class EmitTest
{
private static GetPropertyValueInvoker GetEmitInvoker(Type type, string propertyName)
{
PropertyInfo propInfo = type.GetProperty(propertyName
, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
// Create a DynamicMethod
Type ownerType = type.IsInterface ? typeof ( object ) : type;
bool canSkipChecks = SecurityManager.IsGranted( new ReflectionPermission(ReflectionPermissionFlag.MemberAccess));
DynamicMethod method = new DynamicMethod( string .Empty, typeof ( object )
, new Type[] { typeof ( object ) }, ownerType, canSkipChecks);
// Use ILGenerator to build the method body's IL code
ILGenerator il = method.GetILGenerator();
// Load the first argument of the method call onto stack
il.Emit(OpCodes.Ldarg_0);
// Necessary type conversion
if (type.IsValueType)
il.Emit(OpCodes.Unbox, type);
else
il.Emit(OpCodes.Castclass, type);
// Call the property's get method
il.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod( true ), null );
// If getter return a value type, box it to object
if (propInfo.PropertyType.IsValueType)
{
il.Emit(OpCodes.Box, propInfo.PropertyType);
}
// Return the value of the property
il.Emit(OpCodes.Ret);
// Create a delegate for the client to call the dynamic method
GetPropertyValueInvoker getInvoker =
(GetPropertyValueInvoker)method.CreateDelegate( typeof (GetPropertyValueInvoker));
return getInvoker;
}
// simply cache the dynamic method for the type TestEntity
private static IDictionary < string , GetPropertyValueInvoker > _invokerCache
= new Dictionary < string , GetPropertyValueInvoker > ( 3 );
// Use the dynamic method, which is generated by using Emit every time, to get the property's value
public static object GetValueEmit(Type type, string propertyName, object instance)
{
return EmitTest.GetEmitInvoker(type, propertyName)(instance);
}
// Use the cached dynamic method to get the property's value
public static object GetValueEmitWithCache(Type type, string propertyName, object instance)
{
GetPropertyValueInvoker invoker = null ;
if ( ! _invokerCache.TryGetValue(propertyName, out invoker))
{
invoker = EmitTest.GetEmitInvoker(type, propertyName);
_invokerCache[propertyName] = invoker;
}
return invoker(instance);
}
// Use reflection to get the property's value
public static object GetValueReflect(Type type, string propertyName, object instance)
{
PropertyInfo propInfo = type.GetProperty(propertyName
, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
return propInfo.GetValue(instance, null );
}
}
public delegate object GetPropertyValueInvoker( object obj);
public class EmitTest
{
private static GetPropertyValueInvoker GetEmitInvoker(Type type, string propertyName)
{
PropertyInfo propInfo = type.GetProperty(propertyName
, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
// Create a DynamicMethod
Type ownerType = type.IsInterface ? typeof ( object ) : type;
bool canSkipChecks = SecurityManager.IsGranted( new ReflectionPermission(ReflectionPermissionFlag.MemberAccess));
DynamicMethod method = new DynamicMethod( string .Empty, typeof ( object )
, new Type[] { typeof ( object ) }, ownerType, canSkipChecks);
// Use ILGenerator to build the method body's IL code
ILGenerator il = method.GetILGenerator();
// Load the first argument of the method call onto stack
il.Emit(OpCodes.Ldarg_0);
// Necessary type conversion
if (type.IsValueType)
il.Emit(OpCodes.Unbox, type);
else
il.Emit(OpCodes.Castclass, type);
// Call the property's get method
il.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod( true ), null );
// If getter return a value type, box it to object
if (propInfo.PropertyType.IsValueType)
{
il.Emit(OpCodes.Box, propInfo.PropertyType);
}
// Return the value of the property
il.Emit(OpCodes.Ret);
// Create a delegate for the client to call the dynamic method
GetPropertyValueInvoker getInvoker =
(GetPropertyValueInvoker)method.CreateDelegate( typeof (GetPropertyValueInvoker));
return getInvoker;
}
// simply cache the dynamic method for the type TestEntity
private static IDictionary < string , GetPropertyValueInvoker > _invokerCache
= new Dictionary < string , GetPropertyValueInvoker > ( 3 );
// Use the dynamic method, which is generated by using Emit every time, to get the property's value
public static object GetValueEmit(Type type, string propertyName, object instance)
{
return EmitTest.GetEmitInvoker(type, propertyName)(instance);
}
// Use the cached dynamic method to get the property's value
public static object GetValueEmitWithCache(Type type, string propertyName, object instance)
{
GetPropertyValueInvoker invoker = null ;
if ( ! _invokerCache.TryGetValue(propertyName, out invoker))
{
invoker = EmitTest.GetEmitInvoker(type, propertyName);
_invokerCache[propertyName] = invoker;
}
return invoker(instance);
}
// Use reflection to get the property's value
public static object GetValueReflect(Type type, string propertyName, object instance)
{
PropertyInfo propInfo = type.GetProperty(propertyName
, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
return propInfo.GetValue(instance, null );
}
}
测试用的实体类:
public
class
TestEntity
{
private string _string1;
private int _int1;
private DateTime _datetime1;
public string String1
{
get { return this ._string1; }
set { this ._string1 = value; }
}
public int Int1
{
get { return this ._int1; }
set { this ._int1 = value; }
}
public DateTime Datetime1
{
get { return this ._datetime1; }
set { this ._datetime1 = value; }
}
}
{
private string _string1;
private int _int1;
private DateTime _datetime1;
public string String1
{
get { return this ._string1; }
set { this ._string1 = value; }
}
public int Int1
{
get { return this ._int1; }
set { this ._int1 = value; }
}
public DateTime Datetime1
{
get { return this ._datetime1; }
set { this ._datetime1 = value; }
}
}
测试代码:
TestEntity entity1
=
new
TestEntity();
entity1.String1 = " a string field " ;
entity1.Int1 = 123 ;
entity1.Datetime1 = DateTime.Now;
TestEntity entity2 = new TestEntity();
entity2.String1 = " String 1 in te2 " ;
entity2.Int1 = 999 ;
entity2.Datetime1 = new DateTime( 2009 , 12 , 1 );
int loops = 10000 ;
Stopwatch sw = new Stopwatch();
sw.Start();
for ( int i = 0 ; i < loops; i ++ )
{
EmitTest.GetValueReflect( typeof (TestEntity), " String1 " , entity1);
EmitTest.GetValueReflect( typeof (TestEntity), " Int1 " , entity1);
EmitTest.GetValueReflect( typeof (TestEntity), " Datetime1 " , entity1);
EmitTest.GetValueReflect( typeof (TestEntity), " String1 " , entity2);
EmitTest.GetValueReflect( typeof (TestEntity), " Int1 " , entity2);
EmitTest.GetValueReflect( typeof (TestEntity), " Datetime1 " , entity2);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for ( int i = 0 ; i < loops; i ++ )
{
EmitTest.GetValueEmit( typeof (TestEntity), " String1 " , entity1);
EmitTest.GetValueEmit( typeof (TestEntity), " Int1 " , entity1);
EmitTest.GetValueEmit( typeof (TestEntity), " Datetime1 " , entity1);
EmitTest.GetValueEmit( typeof (TestEntity), " String1 " , entity2);
EmitTest.GetValueEmit( typeof (TestEntity), " Int1 " , entity2);
EmitTest.GetValueEmit( typeof (TestEntity), " Datetime1 " , entity2);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for ( int i = 0 ; i < loops; i ++ )
{
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " String1 " , entity1);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " Int1 " , entity1);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " Datetime1 " , entity1);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " String1 " , entity2);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " Int1 " , entity2);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " Datetime1 " , entity2);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
entity1.String1 = " a string field " ;
entity1.Int1 = 123 ;
entity1.Datetime1 = DateTime.Now;
TestEntity entity2 = new TestEntity();
entity2.String1 = " String 1 in te2 " ;
entity2.Int1 = 999 ;
entity2.Datetime1 = new DateTime( 2009 , 12 , 1 );
int loops = 10000 ;
Stopwatch sw = new Stopwatch();
sw.Start();
for ( int i = 0 ; i < loops; i ++ )
{
EmitTest.GetValueReflect( typeof (TestEntity), " String1 " , entity1);
EmitTest.GetValueReflect( typeof (TestEntity), " Int1 " , entity1);
EmitTest.GetValueReflect( typeof (TestEntity), " Datetime1 " , entity1);
EmitTest.GetValueReflect( typeof (TestEntity), " String1 " , entity2);
EmitTest.GetValueReflect( typeof (TestEntity), " Int1 " , entity2);
EmitTest.GetValueReflect( typeof (TestEntity), " Datetime1 " , entity2);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for ( int i = 0 ; i < loops; i ++ )
{
EmitTest.GetValueEmit( typeof (TestEntity), " String1 " , entity1);
EmitTest.GetValueEmit( typeof (TestEntity), " Int1 " , entity1);
EmitTest.GetValueEmit( typeof (TestEntity), " Datetime1 " , entity1);
EmitTest.GetValueEmit( typeof (TestEntity), " String1 " , entity2);
EmitTest.GetValueEmit( typeof (TestEntity), " Int1 " , entity2);
EmitTest.GetValueEmit( typeof (TestEntity), " Datetime1 " , entity2);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for ( int i = 0 ; i < loops; i ++ )
{
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " String1 " , entity1);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " Int1 " , entity1);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " Datetime1 " , entity1);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " String1 " , entity2);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " Int1 " , entity2);
EmitTest.GetValueEmitWithCache( typeof (TestEntity), " Datetime1 " , entity2);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
结果大致如下:
470
31668
32
这个测试情况下,使用Emit性能大致提高10多倍。