快速构建缓存密钥

时间:2014-10-08 14:22:12

标签: c# .net performance hash .net-4.5

我在Web Api 2应用程序中使用自定义缓存实现。此缓存存储数十万个项目,并且可以在单个API请求中读取多达10,000次。

在分析时,我发现每个项目的缓存密钥的实际构建会显着影响整体性能。

.NET分析的结果:

Sampling Profiler

缓存密钥详细信息:

我正在通过散列字符串来构建项目的密钥。 E.g:

MySystem.MyProject.MyNamespace.MyClass.SomeMethod(44,6948)

这会变成这样的东西,然后在缓存框架中用作密钥(不再使用它 - 参考编辑3):

1bbbfeae-b143-77f2-8381-5ee11f5b9c0c 

显然我需要确保每个密钥的唯一性,但我似乎无法在不引入可能的重复的情况下找到改善性能的方法。

密钥生成器:

public class CacheKeyBuilder
{
    private MethodInterceptionArgs methodArguments;

    public CacheKeyBuilder(MethodInterceptionArgs input)
    {
        methodArguments = input;
    }

    // No longer used - refer to EDIT 3
    public UInt64 GetHashedKey()
    {
        return Hash(GetFriendlyKey());
    }

    public string GetFriendlyKey()
    {
        if (methodArguments.Arguments.OfType<IList>().Any())
        {
            throw new ArgumentOutOfRangeException("Cannot create a keys from IList types");
        }

        var type = methodArguments.Binding.GetType();

        var key = String.Format("{0}.{1}.{2}{3}{4}",
            type.Namespace,
            type.DeclaringType.Name,
            methodArguments.Method.Name,
            type.UnderlyingSystemType.GenericTypeArguments.Select(x => x.Name).ToList().JoinItems("<", ">", ","),
            methodArguments.Arguments.Where(x => x != null).Select(x => x.ToString()).ToList().JoinItems("(", ")", ",")
        );

        return key;
    }

    // No longer used - refer to EDIT 3
    private UInt64 Hash(string key)
    {
        UInt64 hashedValue = 3074457345618258791ul;

        for (int i = 0; i < key.Length; i++)
        {
            hashedValue += key[i];
            hashedValue *= 3074457345618258799ul;
        }

        return hashedValue;
    }
}

考虑:

  • 密钥需要名称空间,完整类型名称,泛型和所有属性值以确保唯一性。
  • String.Format()基本上实现了StringBuilder,因此这应该是构建字符串的最有效方式。
  • 我从this post(Knuth hash?)获得了哈希,这比我之前的实现更快。

有人能发现任何明显的性能提升吗?

修改

根据David和Patryk的评论,另一个考虑因素是我不能硬编码“type”字符串。性能改进需要向后兼容。我必须使用反思。

编辑2:

抱歉,哈希方法意味着返回UInt64。代码已修复。

编辑3:

将哈希键与友好键存储在性能上没有任何区别。因此,我转向使用GetFriendly()。谢谢你们。

1 个答案:

答案 0 :(得分:2)

看起来你正在使用PostSharp。用于缓存的Their own example在编译时将方法名称生成为字符串

您似乎可以同时获得完全限定的类型名称。这将允许昂贵的反射仅在编译时发生。

public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
{
    _methodName = method.Name;
    _typeName = method.Binding.GetType().Namespace...  ..Name; // etc
}

我还会尝试StringBuilder.Append() vs string.Format(),看看是否存在性能差异。