2014-10-08 10 views
3

Я использую пользовательскую реализацию кеша в приложении Web Api 2. Этот кеш хранит сотни тысяч элементов и может быть прочитан от 10 000 раз в одном запросе API.Быстрое создание ключа кэша

При профилировании я обнаружил, что фактическое построение кеша ключей каждого элемента существенно влияет на общую производительность.

Результат от .NET профилирование:

Sampling Profiler

Кэш ключевые детали:

Я здание ключ пункты, путем хэширования строки. Например:

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

Это получает хэшируются в нечто вроде этого, который затем используется в рамках кэширования в качестве ключа (это не больше используется - см EDIT 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 hash?), Которое быстрее, чем мои собственные предыдущие реализации.

Можно ли выявить какие-либо очевидные улучшения в производительности, которые могут быть сделаны?

EDIT:

Еще одно соображение, основанное на Давида и комментарии Patryk, то, что я не могу жесткий код «тип» строка. Улучшения производительности должны быть обратно совместимы. Я должен работать с размышлением.

EDIT 2:

К сожалению, методы хеширования предназначены для возврата UInt64. Код исправлен.

EDIT 3:

Сохранение ключа хешированной против дружественного ключа не сделал никакой разницы в производительности. Таким образом, я перехожу только к GetFriendly(). Спасибо usr.

+0

Функция 'Hash' возвращает строку. Если вы собираетесь использовать строку в качестве ключа, просто используйте оригинальную строку с неповрежденной строкой: 'MySystem.MyProject.MyNamespace.MyClass.SomeMethod (44,6948)'. Тем не менее, я думаю, что ваше узкое место, вероятно, находится в 'GetFriendlyKey()'. Я не вижу ваш скриншот профилирования. Могут ли жестко закодированные строки с добавленными параметрами помочь? –

+2

Отражение, вероятно, здесь не помогает ... –

+1

О, и 10 000 запросов в одном вызове API говорят мне, что вам нужно какое-то хранилище контекста, чтобы свести к минимуму это. Кэширование должно быть главным образом _between_ вызовов. –

ответ

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() против string.Format() и посмотреть, если есть разница Peformance.

+0

Фантастический. Это имеет существенное значение. – davenewza

Смежные вопросы