C# 快速构建缓存密钥

C# 快速构建缓存密钥,c#,.net,performance,hash,.net-4.5,C#,.net,Performance,Hash,.net 4.5,我正在Web Api 2应用程序中使用自定义缓存实现。此缓存存储数十万项,在一个API请求中最多可以读取10000次 在分析方面,我发现每个项的缓存键的实际构建会显著影响总体性能 来自.NET分析的结果: public class CacheKeyBuilder { private MethodInterceptionArgs methodArguments; public CacheKeyBuilder(MethodInterceptionArgs input) {

我正在Web Api 2应用程序中使用自定义缓存实现。此缓存存储数十万项,在一个API请求中最多可以读取10000次

在分析方面,我发现每个项的缓存键的实际构建会显著影响总体性能

来自.NET分析的结果:

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;
    }
}

缓存密钥详细信息:

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;
    }
}
我正在通过散列一个字符串来构建项的键。例如:

MySystem.MyProject.MyNamespace.MyClass.SomeMethod(44,6948)
这将被散列成如下内容,然后在缓存框架中用作键(不再使用此键-请参阅编辑3):

显然,我需要确保每个键的唯一性,但我似乎无法找到一种方法来提高性能,而不引入可能的重复

密钥生成器:

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;
    }
}
公共类CacheKeyBuilder
{
私有MethodInterceptionArgs methodArguments;
public CacheKeyBuilder(MethodInterceptionArgs输入)
{
方法参数=输入;
}
//不再使用-请参阅编辑3
公共UInt64 GetHashedKey()
{
返回散列(GetFriendlyKey());
}
公共字符串GetFriendlyKey()
{
if(methodArguments.Arguments.OfType().Any())
{
抛出新ArgumentOutOfRangeException(“无法从IList类型创建密钥”);
}
var type=methodArguments.Binding.GetType();
var key=String.Format(“{0}.{1}.{2}{3}{4}”,
类型。命名空间,
type.DeclaringType.Name,
methodArguments.Method.Name,
type.underyingSystemType.GenericTypeArguments.Select(x=>x.Name).ToList().JoinItems(“,”,”),
methodArguments.Arguments.Where(x=>x!=null)。选择(x=>x.ToString()).ToList().JoinItems((“,”,“,”)
);
返回键;
}
//不再使用-请参阅编辑3
专用UInt64哈希(字符串密钥)
{
UInt64哈希值=3074457345618258791ul;
for(int i=0;i
注意事项:

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
    ,因此这应该是构建字符串的最有效方法
  • 我从(Knuth hash?)获得了哈希,这比我以前的实现更快
有人能发现任何明显的性能改进吗

编辑:

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;
    }
}
另一个考虑是,基于David和Patryk的评论,我不能硬编码“type”字符串。性能改进需要向后兼容。我必须反思

编辑2:

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;
    }
}
很抱歉,哈希方法将返回
UInt64
。代码已修复

编辑3:

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;
    }
}

存储散列密钥和友好密钥在性能上没有任何区别。因此,我将使用
GetFriendly()
移动到唯一的应用程序。谢谢usr。

看起来您正在使用PostSharp。for caching在编译时将方法名生成为字符串

public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
{
    _methodName = method.Name;
    _typeName = method.Binding.GetType().Namespace...  ..Name; // etc
}
似乎可以同时获得完全限定的类型名。这将允许只在编译时发生昂贵的反射

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

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

您的
Hash
函数返回一个字符串。如果要使用字符串作为键,则只需使用原始的未剪切字符串:
MySystem.MyProject.MyNamespace.MyClass.SomeMethod(446948)
。也就是说,我认为您的瓶颈可能在
GetFriendlyKey()
中。我看不到你的分析截图。带有附加参数的硬编码字符串会有帮助吗?反射在这里可能也没有帮助…哦,一个API调用中的10000次查找告诉我,您需要某种上下文存储来最小化这一点。缓存应该主要在调用之间进行。为什么要对生成的密钥进行散列?为什么散列的结果与作为示例(Guid)提供的格式不匹配?太棒了。这有很大的不同。