Improving performance by caching or delegate call?

前端 未结 4 867
孤街浪徒
孤街浪徒 2021-01-23 10:37

I am trying to improve performance in the code below and kinda know how but not sure which is the best approach. The first hit will take longer but subsequent hits should be qui

4条回答
  •  猫巷女王i
    2021-01-23 11:17

    DynamicMethods or ExpressionTrees will be much* faster than reflection. You could build a cache of all property getter/setters for a type, and then cache that information in a Dictionary (or ConcurrentDictionary) with type as the key.

    • Expression Tree Basics
    • Dynamic Methods

    Flow

    • Discover type information (e.g. on app startup).
    • Compile dynamic methods for each property (do all properties at once).
    • Store those methods in a metadata class (example follows).
    • Cache the metadata somewhere (even a static field is fine, as long as access is synchronized). Use the type as the key.
    • Get the metadata for the type when needed.
    • Find the appropriate getter/setter.
    • Invoke, passing the instance on which you wish to act.

    // Metadata for a type
    public sealed class TypeMetadata {
    
        // The compiled getters for the type; the property name is the key
        public Dictionary> Getters {
            get;
            set;
        }
    
        // The compiled setters for the type; the property name is the key
        public Dictionary> Setters {
            get;
            set;
        }
    }
    
    // rough invocation flow
    var type = typeof( T);
    var metadata = _cache[type];
    
    var propertyName = "MyProperty";
    var setter = metadata[propertyName];
    
    var instance = new T();
    var value = 12345;
    setter( instance, value );
    

    Example Setter

    Excerpted from Dynamic Method Implementation (good article on the subject).

    I can't vouch that this exact code works, but I've written very similar code myself. If you aren't comfortable with IL, definitely consider an expression tree instead.

    public static LateBoundPropertySet CreateSet(PropertyInfo property)
    {
        var method = new DynamicMethod("Set" + property.Name, null, new[] { typeof(object), typeof(object) }, true);
        var gen = method.GetILGenerator();
    
        var sourceType = property.DeclaringType;
        var setter = property.GetSetMethod(true);
    
        gen.Emit(OpCodes.Ldarg_0); // Load input to stack
        gen.Emit(OpCodes.Castclass, sourceType); // Cast to source type
        gen.Emit(OpCodes.Ldarg_1); // Load value to stack
        gen.Emit(OpCodes.Unbox_Any, property.PropertyType); // Unbox the value to its proper value type
        gen.Emit(OpCodes.Callvirt, setter); // Call the setter method
        gen.Emit(OpCodes.Ret);
    
        var result = (LateBoundPropertySet)method.CreateDelegate(typeof(LateBoundPropertySet));
    
        return result;
    }
    

    *25-100x faster in my experience

提交回复
热议问题