Richie

Sometimes at night when I look up at the stars, and see the whole sky just laid out there, don't you think I ain't remembering it all. I still got dreams like anybody else, and ever so often, I am thinking about how things might of been. And then, all of a sudden, I'm forty, fifty, sixty years old, you know?

Emit与Reflection性能对比测试

使用Emit代替反射性能提升比较多,不过使用Emit需要将DynamicMethod缓存起来,缓存太多也不好,所以可以使用一些折中方法。
例如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 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; }
    }
}

测试代码:
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(2009121);

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多倍。

posted on 2007-12-13 23:45  riccc  阅读(2290)  评论(3编辑  收藏  举报

导航