【问题标题】:Building a Cache Key quickly快速构建缓存键
【发布时间】:2014-12-03 06:07:13
【问题描述】:

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

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

.NET 分析结果:

缓存关键细节:

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

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 哈希?)得到哈希,这比我自己以前的实现要快。

谁能发现任何明显的性能改进?

编辑:

另一个基于 David 和 Patryk 的 cmets 的考虑是我不能硬编码“类型”字符串。性能改进需要向后兼容。我必须使用反射。

编辑 2:

抱歉,哈希方法旨在返回 UInt64。代码已修复。

编辑 3:

存储散列密钥与友好密钥对性能没有影响。因此,我将转向唯一使用GetFriendly()。谢谢楼主。

【问题讨论】:

  • 您的Hash 函数返回一个字符串。如果您要使用字符串作为键,则只需使用原始未散列的字符串:MySystem.MyProject.MyNamespace.MyClass.SomeMethod(44,6948)。也就是说,我认为您的瓶颈可能在GetFriendlyKey()。我看不到你的分析截图。附加参数的硬编码字符串会有帮助吗?
  • 反思在这里可能也无济于事......
  • 哦,一个 API 调用中的 10,000 次查找告诉我您需要某种上下文存储来最小化这种情况。缓存应该主要在之间调用。
  • 你为什么要散列你生成的密钥?
  • 为什么 Hash 的结果与你给出的示例格式(Guid)不匹配?

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


【解决方案1】:

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

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

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

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

【讨论】:

  • 太棒了。这有很大的不同。
猜你喜欢
  • 2019-06-19
  • 2014-03-28
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
  • 2016-06-08
  • 1970-01-01
  • 2019-01-14
相关资源
最近更新 更多