Quickly create a cache key

I am using a custom cache implementation in Web Api 2. This cache stores hundreds of thousands of items and can be read 10,000 times or more in a single API request.

In profiling, I found that actually building the key cache of each item has a significant impact on overall performance.

Result from .NET profiling:

Sampling profiler

Cache key details:

I am creating an item key by hashing a string. For example:

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

      

This becomes hashed into something like this, which is then used in the caching framework as a key (this is no longer used - see EDIT 3):

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

      

Obviously I need to enforce the uniqueness of each key, but I cannot find a way to improve performance here without introducing possible duplication.

Key constructor:

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

      

Questions:

  • To be unique, a keyword requires a namespace, a fully qualified type name, common elements, and all property values.
  • String.Format()

    essentially implements StringBuilder

    , so this should be the most efficient way to construct strings.
  • I got a hash from this post (Knuth hash?) Which is faster than my own previous implementations.

Are there any obvious performance improvements that can be made?

EDIT:

Another consideration, based on the comments by David and Patrick, is that I cannot hardcode the type string. Performance improvements should be backward compatible. I have to work with reflection.

EDIT 2:

Sorry, hash methods are meant to return UInt64

. Fixed code.

EDIT 3:

Storing the hashed key and friendly key did not affect performance. So I only go to GetFriendly()

. Thanks usr.

+3


source to share


1 answer


It looks like you are using PostSharp. Their own example for caching generates the method name as a string at compile time.

It seems you can get the full type name at the same time. This would avoid expensive compile-time reflection.



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

      

I would also try StringBuilder.Append()

vs string.Format()

and see if there is a shape difference.

+2


source







All Articles