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:
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 implementsStringBuilder
, 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.
source to share
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.
source to share