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

本文关键字:调用 高性能 缓存 | 更新日期: 2023-09-27 18:02:26

我正在努力提高下面代码的性能,有点知道如何,但不确定哪是最好的方法。第一次命中会花更长的时间,但随后的命中会更快。现在,我可以缓存T(这里T是一个类),然后检查缓存,看看"T"是否存在,如果存在,继续获取它的相关信息(NamedArguments),并遍历每个NamedArguments,最后如果条件匹配,继续设置当前属性的值。

我只是想让它更有效率和性能。什么好主意吗?

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

  • Expression Tree Basics
  • 动态方法

流动
  • 发现类型信息(例如在应用程序启动时)
  • 为每个属性编译动态方法(一次执行所有属性)
  • 将这些方法存储在元数据类中(示例如下)。
  • 将元数据缓存到某个地方(即使是静态字段也可以,只要访问是同步的)。使用类型作为键。
  • 在需要时获取类型的元数据
  • 查找合适的getter/setter。
  • 调用,传递您希望操作的实例。

// 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 );

例子Setter

摘自动态方法实现(关于这个主题的好文章)。

我不能保证这段完全正确的代码有效,但是我自己写过非常类似的代码。如果你不习惯使用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-100倍

反射在循环中是出了名的慢,所以某种缓存可能会有所帮助。但是要决定缓存什么,应该度量。俗话说:"过早优化是万恶之源";你应该确定你真的需要优化,以及优化的具体内容。

对于更具体的建议,属性是在编译时附加的,因此您可以缓存类型和它的propertyinfo列表。

我也有过一次类似的问题-反射非常慢。我使用缓存,就像你计划的那样,性能增长超过10倍。这再也不是性能的瓶颈

我之前创建了类似的逻辑,在那里我为遇到的每种类型缓存了一个"执行计划"。对于随后的运行来说,这无疑是更快的,但您必须对您的场景进行分析,以确定是否值得额外的代码复杂性和内存使用。