Emit与Reflection性能对比测试
使用Emit代替反射性能提升比较多,不过使用Emit需要将DynamicMethod缓存起来,缓存太多也不好,所以可以使用一些折中方法。
例如NHibernate,使用Emit读取/设置实体属性时,生成的DynamicMethod每次读取/设置全部的属性值,读取时返回一个object[],设置时提供一个包含各个属性值的object[]数组。在SELECT时使用这种方式一次设置好所有属性;在INSERT、UPDATE时则一次读出全部属性;在DELETE时则可以直接使用反射从实体上读取identity属性,因为从应用层面总体来看反射的性能还是比较好的。
Emit跟反射的性能测试对比如下:
Emit、Reflection代码:
测试用的实体类:
测试代码:
结果大致如下:
470
31668
32
这个测试情况下,使用Emit性能大致提高10多倍。
例如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多倍。