简体   繁体   English

通过缓存或委托调用来提高性能?

[英]Improving performance by caching or delegate call?

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 quicker.第一次命中需要更长的时间,但随后的命中应该更快。 Now, I could cache T (where T is a class) and then check the cache to see if "T" exists, if so - go ahead and get its related information (NamedArguments) and go through each of the NamedArguments and finally if the criteria matches, go ahead and set the value of the current property.现在,我可以缓存 T(其中 T 是一个类),然后检查缓存以查看“T”是否存在,如果存在 - 继续获取其相关信息(NamedArguments)并遍历每个 NamedArguments,最后如果条件匹配,继续并设置当前属性的值。

I just want to make it more efficient and performant.我只是想让它更有效率和性能。 Any ideas?有任何想法吗?

var myProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).Where(prop => Attribute.IsDefined(prop, typeof(MyCustomAttribute)) && prop.CanWrite && prop.GetSetMethod() != null);

foreach (var currentProperty in myProps)
{
    foreach (var currentAttributeForProperty in currentProperty.GetCustomAttributesData())
    {
        foreach (var currentNamedArgument in currentAttributeForProperty.NamedArguments)
        {
            if (string.Equals(currentNamedArgument.MemberInfo.Name, "PropName", StringComparison.OrdinalIgnoreCase))
            {
                currentAttribParamValue = currentNamedArgument.TypedValue.Value == null ? null : currentNamedArgument.TypedValue.Value.ToString();

                // read the reader for the currentAttribute value
                if (reader.DoesFieldExist(currentAttribParamValue))
                {
                    var dbRecordValue = reader[currentAttribParamValue] == DBNull.Value ? null : reader[currentAttribParamValue];

                    // set it in the property
                    currentProperty.SetValue(val, dbRecordValue, null);
                }
                break;
            }
        }
    }
}

DynamicMethods or ExpressionTrees will be much* faster than reflection. DynamicMethods 或 ExpressionTrees 将比反射快得多*。 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.您可以为某个类型构建所有属性 getter/setter 的缓存,然后将该信息缓存在Dictionary (或ConcurrentDictionary )中,并将 type 作为键。

Flow流动

  • Discover type information (eg 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.找到合适的 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.如果您对 IL 不满意,请务必考虑使用表达式树。

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 *根据我的经验,速度要快 25-100 倍

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.对于更具体的建议,在编译时附加属性,例如,您可以缓存类型及其属性信息列表。

I also had similar problem once - reflection is extreemely slow.我也遇到过类似的问题 - 反射非常慢。 I used caching, like you are planning and performance grow more than 10 times .我使用了缓存,就像你在计划和性能增长超过10 倍 It was never again be a bottleneck in performance.它再也不会成为性能的瓶颈。

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.对于后续运行来说肯定更快,但您必须分析您的场景,以查看是否值得额外的代码复杂性和内存使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM