2013-08-27 36 views
9

Tôi đang sử dụng từ điển để tích lũy số lần xuất hiện của khóa và do đó, hoạt động chính là viết cặp khóa-giá trị trong đó giá trị là giá trị trước đó cộng với một hoặc chỉ nếu không có giá trị trước đó. Tuy nhiên, điều này đòi hỏi hai hoạt động từ điển riêng biệt (đọc và ghi) khi tôi chỉ có thể làm một (AddOrUpdate).Cập nhật ràng buộc hiệu quả trong từ điển .NET

Tôi nhận thấy rằng từ điển đồng thời hỗ trợ AddOrUpdate nhưng thông thường chung chung Dictionary không xuất hiện.

Do đó, từ điển tham chiếu đến int có thể thay đổi sẽ nhanh hơn. Tuy nhiên, điều này giới thiệu tài liệu tham khảo không cần thiết có nghĩa là phân bổ đống và viết các rào cản. Vì vậy, tôi đoán nó có thể làm tốt hơn đáng kể nhưng tôi không thể nhìn thấy như thế nào mà không cần viết lại Dictionary từ đầu. Tôi có đúng không?

+0

Vì vậy, bạn đang cố gắng loại bỏ một trong các lần tra cứu trong trường hợp thêm hoặc cập nhật? – mydogisbox

+0

Từ điển đồng thời có vẻ khá hiệu quả trong nhiều trường hợp, bạn đã kiểm tra xem nó có cung cấp đủ hiệu suất cho kịch bản của bạn không? – Alex

+0

bạn có thể sắp xếp các khóa-giá trị không? Tôi đoán hầu hết sẽ là O (n log n), do đó bạn có thể phải kiểm tra hiệu năng tốt nhất – Carsten

Trả lời

2

Một cập nhật từ điển không đòi hỏi nhiều tra cứu nếu bạn đang sử dụng các loại tài liệu tham khảo:

Giả sử bạn có một Dictionary<string, Foo>, nơi Foo là một loại tài liệu tham khảo và bao gồm một tài sản Count:

void UpdateCount(string key) 
{ 
    Foo f; 
    if (dict.TryGetValue(key, out f)) 
    { 
     // do the update 
     ++f.Count; 
    } 
    else 
    { 
     dict[key] = 1; 
    } 
} 

Nếu giá trị của bạn là các loại giá trị ... tốt, sau đó bạn phải đối phó với ngữ nghĩa loại giá trị. Và điều đó bao gồm việc phải thực hiện hai lần tra cứu.

Điều đó nói rằng tra cứu từ điển khá nhanh chóng. Nếu điều này gây ra cho bạn một vấn đề, bạn phải có rất nhiều lần xuất hiện để đếm.

3

Như Jim Mischel đã đề cập - không thể thực hiện tra cứu đơn lẻ để thay đổi giá trị mặt hàng của từ điển. ConcurrentDictionary.AddOrUpdate phương pháp làm nhiều hơn là một hoạt động tra cứu (nguồn phản ánh):

public TValue AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory) 
{ 
    TValue local2; 
    if (key == null) 
    { 
     throw new ArgumentNullException("key"); 
    } 
    if (updateValueFactory == null) 
    { 
     throw new ArgumentNullException("updateValueFactory"); 
    } 
    do 
    { 
     TValue local3; 
     while (this.TryGetValue(key, out local3)) 
     { 
      TValue newValue = updateValueFactory(key, local3); 
      if (this.TryUpdate(key, newValue, local3)) 
      { 
       return newValue; 
      } 
     } 
    } 
    while (!this.TryAddInternal(key, addValue, false, true, out local2)); 
    return local2; 
} 

Tôi đã thực hiện thử nghiệm hiệu suất với từ điển đồng thời và ditcionary đơn giản: mở rộng AddOrUpdate

cho IDictionary:

public static class DictionaryExtensions 
{ 
    public static void AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue initValue, Func<TKey, TValue, TValue> updateFunc) 
    { 
     TValue value; 
     value = dict.TryGetValue(key, out value) ? updateFunc(key, value) : initValue; 

     dict[key] = value; 
    } 
} 

Kiểm tra:

static void Main(string[] args) 
{ 
    const int dictLength = 100000; 
    const int testCount = 1000000; 

    var cdict = new ConcurrentDictionary<string, int>(GetRandomData(dictLength)); 
    var dict = GetRandomData(dictLength).ToDictionary(x => x.Key, x => x.Value); 

    var stopwatch = new Stopwatch(); 
    stopwatch.Start(); 
    foreach (var pair in GetRandomData(testCount)) 
     cdict.AddOrUpdate(pair.Key, 1, (x, y) => y+1);   

    stopwatch.Stop(); 
    Console.WriteLine("Concurrent dictionary: {0}", stopwatch.ElapsedMilliseconds); 

    stopwatch.Reset(); 
    stopwatch.Start(); 

    foreach (var pair in GetRandomData(testCount)) 
     dict.AddOrUpdate(pair.Key, 1, (x, y) => y+1); 

    stopwatch.Stop(); 
    Console.WriteLine("Dictionary: {0}", stopwatch.ElapsedMilliseconds); 
    Console.ReadLine(); 
} 

static IEnumerable<KeyValuePair<string, int>> GetRandomData(int count) 
{ 
    const int constSeed = 100; 
    var randGenerator = new Random(constSeed); 
    return Enumerable.Range(0, count).Select((x, ind) => new KeyValuePair<string, int>(randGenerator.Next().ToString() + "_" + ind, randGenerator.Next())); 
} 

Kết quả thử nghiệm trên môi trường của tôi (mili giây):

ConcurrentDictionary: 2504 
Dictionary: 1351 
5

Bạn có thể làm một cái gì đó như thế này:

private class Counter 
{ 
    public string Key  { get ; set ; } 
    public int Frequency { get ; set ; } 
} 

... 

Dictionary<string,Counter> frequencyTable = new Dictionary<string,Counter>() ; 

... 

string someKey = GetKeyToLookup() ; 
Counter item = null ; 
bool hit = frequencyTable.TryGetValue(someKey,out item) ; 
if (!hit) 
{ 
    item = new Counter{ Key=someKey,Frequency=0 } ; 
} 
++ item.Frequency ; 

Nếu đó là không đủ tốt, tại sao viết của riêng bạn? Sử dụng hiệu suất cao C5 Collections Library. Nó hoàn toàn miễn phí (ban đầu được Microsoft tài trợ, trên thực tế), được xây dựng trên các giao diện System.Collections.Generic của Microsoft và có các bộ từ điển, bộ và túi hỗ trợ FindOrAdd() ngữ nghĩa.

+0

Vâng, đó là chính xác những gì tôi có nghĩa là "từ điển tham chiếu đến ints có thể thay đổi nhanh hơn" nhưng điều đó giới thiệu các tham chiếu không cần thiết có nghĩa là phân bổ đống và viết các rào cản. –

+0

@JonHarrop Bạn đã thử chưa? C5 thực sự hiệu quả hơn cho nhiệm vụ này? Tra cứu thứ hai hoặc loại tham chiếu có tốn kém hơn không? – Goswin

+0

Tôi đã thử nó với mã của riêng tôi (không phải C5) và từ điển của các tài liệu tham khảo có thể thay đổi được nhanh hơn mà tra cứu đôi trên một từ điển các giá trị. Tra cứu thứ hai là tốn kém hơn. Tuy nhiên, một từ điển cho phép add-in-place sẽ là giải pháp nhanh nhất, tất nhiên. –

Các vấn đề liên quan