2012-06-10 1 views
3

Я реализую кеш функции, которая называется сотни миллионов раз. Размер кеша составляет десятки миллионов единиц. В настоящее время он реализуется с использованием Dictionary, и поиск в нем занимает значительное количество времени.Можно ли получить ссылку на словарь в C#?

Возможно ли получить ссылку на всю пару в Dictionary, а не только на значение, поэтому я могу проверить, существует ли значение, проверить его (и, возможно, обновить), если он использует один поиск ?

В настоящее время у меня есть что-то вроде этого:

int val; 
if (cache.TryGetValue(key, out val)) 
    if (val < newVal) cache[key] = newVal; 
    else return val; 
else 
    cache.Add(key, newVal); 

Я хотел бы получить это:

Pair pair = cache.GetPair(key); 
if (pair != null) 
    if (pair.Value < newVal) pair.Value = newVal; 
    else return pair.Value; 
else 
    cache.Add(key, newVal); 

Если есть альтернативная структура данных, которая позволяет это, я был бы рад услышать об этом тоже.

Заранее благодарен!

+2

«поиски в нем принимают значительное количество времени." В самом деле? Что такое «значительное количество времени»? Секунды? Миллисекунды? Микросекунды? Вы уверены, что правильно реализовали «GetHashCode» и «Equals»? Можете ли вы разместить свой код? –

+2

'KeyValuePair' неизменен, поэтому это не сработает. Кроме того, словарный поиск должен быть O (1). Если это действительно занимает много времени, что-то не так. – Blorgbeard

+1

Единственный поиск - O (1) и быстрый. Но миллионы поисковых запросов имеют значительную совокупную стоимость. Обычные предупреждения о преждевременной оптимизации здесь не применяются. – usr

ответ

4

Это вдохновлено ответом Маре Infinitus. Если предположить, что cache переменные теперь Dictionary<string, int> вы можете изменить его в Dictionary<string, MutableInt32> где MutableInt32 написано так:

// wraps an int that may change 
class MutableInt32 
{ 
    public int Value; 
} 

Тогда вы могли бы изменить свой код

MutableInt32 val; 
if (cache.TryGetValue(key, out val)) 
    if (val.Value < newVal) val.Value = newVal; 
    else ... 
+0

, конечно, с указанным сценарием достаточно одного mutableInt! но есть уже принятый ответ :( –

+0

@MareInfinitus Stack Overflow оценивает наши новые ответы, хотя, как я полагаю, уже принят один ответ. Мне действительно не нужно хранить дополнительную копию ключа в изменяемом классе-оболочке. Я отправил свой собственный ответ. В любом случае невозможно изменить ключ, потому что его хеш-код уже сохранен в «Словаре <,>'. Словарь <,> 'будет поврежден, если ** ключ ** изменен! –

+0

и i как и ваша идея, поэтому я проголосовал за нее! :) проведение дополнительного MutableTuple просто переборщило, вы правы. –

2

Ваша идея в том, что она будет разрезать половину числа операций хэш-поиска и поиска в словаре. Я сам сравнивал такие вещи, и Словарь не так быстро, как думают люди.

К сожалению, встроенный словарь не поддерживает это. Даже обходной путь.

Вы можете реализовать свою собственную хэш-таблицу и сделать это самостоятельно. Правовые вопросы в стороне, вы можете начать с реализации Словаря и добавить метод GetAndUpdateOrCreate.

+0

Возможно ли, что собственная реализация IDictionary выполнит эту работу? Если у вас столько пар, собственный алгоритм сортировки по байкам может быть быстрее, чем общий. –

+1

Это хорошая идея. Вы можете заставить JIT сделать немного лучшую работу, жестко кодируя параметры типа (вместо создания общего словаря).Энди, возможно, избавится от операции modulo и вместо этого использует размер таблицы с двумя размерами, поэтому вы можете использовать и-bitmask для вычисления индекса слота. – usr

2

Вы можете, конечно, хранить пар в словаре!

public class KeyValueTuple 
{ 
    private string key; 
    private int value; 

    public KeyValueTuple(string key, int value) 
    { 
     this.key = key; 
     this.value = value; 
    } 
} 

public class BigDataCache 
{ 
    private Dictionary<string, KeyValueTuple> cache; 

    public BigDataCache() 
    { 
     cache = new Dictionary<string, KeyValueTuple>(); 

     cache.Add("entry1", new KeyValueTuple("entry1", 1)); 
     cache.Add("entry2", new KeyValueTuple("entry2", 2)); 
     cache.Add("entry3", new KeyValueTuple("entry3", 3)); 
    } 

    public KeyValueTuple GetTuple(string key) 
    { 
     KeyValueTuple value = null; 

     if (cache.TryGetValue(key, out value)) 
     { 
      return value; 
     } 

     return null; 
    } 
} 

public void SomeMethod() 
{ 
    BigDataCache d = new BigDataCache(); 

    var value1 = d.GetTuple("entry1"); 
    var value2 = d.GetTuple("entryNotValid"); 
} 
+3

Кортежи неизменяемы, поэтому я не вижу, где здесь большое ускорение. Замена кортежа с изменяемой структурой может служить потребностям OP. – spender

+0

Это также значительно увеличит размер словаря, так как значения будут просто ints, а клавиши - это строки (кэш уже занимает гигабайты оперативной памяти, поэтому это проблема). Но это определенно хороший подход) – SanD

+0

Многих оправданий здесь от меня! не видел, что кортеж неизменен. –