通过缓存或委托呼叫来提高性能?

时间:2013-03-04 19:28:52

标签: c# reflection

我正在努力提高下面代码的性能,并且知道如何但不确定哪种方法最好。 第一次打击需要更长时间,但后续命中应该更快。现在,我可以缓存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;
            }
        }
    }
}

4 个答案:

答案 0 :(得分:3)

DynamicMethods或ExpressionTrees将比反射快得多。您可以为类型构建所有属性getter / setter的缓存,然后将该信息缓存在Dictionary(或ConcurrentDictionary)中,并将类型作为键。

流量

  • 发现类型信息(例如在应用启动时)。
  • 为每个属性编译动态方法(一次执行所有属性)。
  • 将这些方法存储在元数据类中(示例如下)。
  • 在某处缓存元数据(即使静态字段很好,只要访问是同步的)。使用类型作为密钥。
  • 在需要时获取该类型的元数据。
  • 找到合适的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

摘自Dynamic Method Implementation(关于此主题的好文章)。

我无法保证这个完全代码有效,但我自己编写了非常相似的代码。如果您对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倍

答案 1 :(得分:2)

反射在循环中出了名的慢,所以某种缓存可能会有所帮助。但要确定要缓存的内容,您应该衡量。正如俗名所说:“过早优化是一切罪恶的根源”;你应该确保你真的需要优化以及究竟要优化什么。

对于更具体的建议,属性在编译时附加,因此您可以缓存一个类型及其propertyInfos列表。

答案 2 :(得分:1)

我曾经遇到过类似的问题 - 反思速度非常慢。 我使用缓存,就像你在计划和性能增长超过 10次。它再也不是性能瓶颈了。

答案 3 :(得分:0)

在我为遇到的每种类型缓存“执行计划”之前,我已经创建了类似的逻辑。对于后续运行来说,它肯定更快,但您必须对您的场景进行分析,以确定它是否值得额外的代码复杂性和内存使用。