Improving performance by caching or delegate call?

前端 未结 4 861
孤街浪徒
孤街浪徒 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条回答
  • 2021-01-23 11:16

    I've created similar logic before where I've cached an 'execution plan' for each type encountered. It was definitely faster for subsequent runs but you would have to profile your scenario to see whether it's worth the extra code complexity and memory usage.

    0 讨论(0)
  • 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<T> {
    
        // The compiled getters for the type; the property name is the key
        public Dictionary<string, Func<T, object>> Getters {
            get;
            set;
        }
    
        // The compiled setters for the type; the property name is the key
        public Dictionary<string, Action<T, object>> 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

    0 讨论(0)
  • 2021-01-23 11:25

    I also had similar problem once - reflection is extreemely slow. I used caching, like you are planning and performance grow more than 10 times. It was never again be a bottleneck in performance.

    0 讨论(0)
  • 2021-01-23 11:28

    Reflection is notoriously slow in loops, so some kind of caching would probably help. But to decide what to cache, you should measure. As the famous saying goes: "premature optimization is the root of all evil"; you should make sure that you really need to optimize and what exactly to optimize.

    For a more concrete advice, attributes are attached at compile time, so you could cache a type and a list of its propertyInfos for example.

    0 讨论(0)
提交回复
热议问题