Reputation: 40002
I am using a custom cache implementation in a Web Api 2 application. This cache stores hundreds of thousands of items and can be read from up to 10,000 times in a single API request.
On profiling, I have found that the actual building of each items' cache key is significantly affecting overall performance.
Result from .NET profiling:
Cache key details:
I am building a the items' key by hashing a string. E.g:
MySystem.MyProject.MyNamespace.MyClass.SomeMethod(44,6948)
This gets hashed into something like this, which is then used in the caching framework as the key (this is not longer used - refer to EDIT 3):
1bbbfeae-b143-77f2-8381-5ee11f5b9c0c
Obviously I need to ensure uniqueness on each key, but I can't seem to find a way to improve the performance here without introducing possible duplication.
The key builder:
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;
}
}
Considerations:
String.Format()
essentially implements a StringBuilder
, so this should be the most efficient way of building strings.Can anyone spot any evident performance improvements that can be made?
EDIT:
Another consideration, based on David and Patryk's comments, is that I cannot hard code the "type" string. The performance improvements need to be backwards compatible. I have to work with reflection.
EDIT 2:
Sorry, the hash methods are meant to return UInt64
. Code fixed.
EDIT 3:
Storing the hashed key vs the friendly key has made no difference in performance. Thus, I am moving to the only using GetFriendly()
. Thanks usr.
Upvotes: 3
Views: 4106
Reputation: 3813
It looks like your using PostSharp. Their own example for caching generates the method name as a string at compile time.
It seems you could get the fully-qualified type name at the same time. This would allow the expensive reflection only to occur at compile time.
public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
{
_methodName = method.Name;
_typeName = method.Binding.GetType().Namespace... ..Name; // etc
}
I would also try the StringBuilder.Append()
vs string.Format()
and see if there's a peformance difference.
Upvotes: 2