2014-10-08 103 views
3

我在Web Api 2應用程序中使用自定義緩存實現。此緩存可存儲數十萬個項目,並可在單個API請求中讀取多達10,000次。快速構建緩存鍵

在剖析時,我發現每個項目緩存鍵的實際構建都會顯着影響整體性能。從.NET分析

結果:

Sampling Profiler

緩存關鍵細節:

我建立一個項目通過散列字符串鍵。例如:

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散列?)得到散列,比我以前的實現快。

任何人都可以發現任何明顯的性能改進嗎?

編輯:

另一個考慮的基礎上,大衛和Patryk的評論,是我不能硬編碼的「類型」的字符串。性能改進需要向後兼容。我必須反思。

編輯2:

對不起,哈希方法是爲了返回UInt64。代碼固定。

編輯3:

存放哈希鍵VS友好鑰匙已在性能上沒有差異。因此,我正在轉向唯一使用GetFriendly()。謝謝你。

+0

你的'Hash'函數返回一個字符串。如果您打算使用字符串作爲鍵,那麼只需使用原始的非加入字符串:'MySystem.MyProject.MyNamespace.MyClass.SomeMethod(44,6948)'。也就是說,我認爲你的瓶頸可能在'GetFriendlyKey()'中。我看不到你的截圖。具有附加參數的硬編碼字符串可以幫助嗎? – 2014-10-08 14:27:22

+2

反思可能對此無濟於事...... – 2014-10-08 14:28:36

+1

哦,在一次API調用中有10,000次查找告訴我您需要某種上下文存儲來最小化這種情況。緩存應該主要是_間的調用。 – 2014-10-08 14:28:39

回答

2

它看起來像你使用PostSharp。用於緩存的Their own example在編譯時間處生成方法名稱作爲字符串

看來你可以同時獲得完全限定的類型名稱。這將允許昂貴的反射僅在編譯時發生。

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

我也想試試StringBuilder.Append() VS string.Format(),看看是否有一個peformance差異。

+0

太棒了。這有很大的不同。 – davenewza 2014-10-08 17:55:37