Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.ExternalEvaluation/3.3/EvaluationCache.cs @ 6193

Last change on this file since 6193 was 6188, checked in by epitzer, 14 years ago

improve synchronization of cache (#1516)

  • set locks per instance instead of static
  • remove failed evaluations from active set
File size: 9.4 KB
RevLine 
[6140]1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
[6169]19 *
20 * The LRU cache is based on an idea by Robert Rossney see
21 * <http://csharp-lru-cache.googlecode.com>.
[6140]22 */
23#endregion
24
25using System;
26using System.Collections.Generic;
27using System.Linq;
[6183]28using System.Threading;
[6140]29using HeuristicLab.Common;
30using HeuristicLab.Common.Resources;
31using HeuristicLab.Core;
[6169]32using HeuristicLab.Data;
33using HeuristicLab.Parameters;
[6140]34using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
35namespace HeuristicLab.Problems.ExternalEvaluation {
36
37  [Item("EvaluationCache", "Cache for external evaluation values")]
38  [StorableClass]
[6169]39  public class EvaluationCache : ParameterizedNamedItem {
[6140]40
[6169]41    #region Types
42    private class CacheEntry {
43
44      public string Key { get; private set; }
45      public double Value { get; set; }
46
47      public CacheEntry(string key, double value) {
48        Key = key;
49        Value = value;
50      }
51
52      public CacheEntry(string key) : this(key, 0) { }
53
54      public override bool Equals(object obj) {
55        CacheEntry other = obj as CacheEntry;
56        if (other == null)
57          return false;
58        return Key.Equals(other.Key);
59      }
60
61      public override int GetHashCode() {
62        return Key.GetHashCode();
63      }
64
65      public override string ToString() {
66        return string.Format("{{{0} : {1}}}", Key, Value);
67      }
68    }
69
[6140]70    public delegate double Evaluator(SolutionMessage message);
[6169]71    #endregion
[6140]72
[6169]73    #region Fields
74    private LinkedList<CacheEntry> list;
75    private Dictionary<CacheEntry, LinkedListNode<CacheEntry>> index;
[6188]76    private HashSet<string> activeEvaluations = new HashSet<string>();
77    private object cacheLock = new object();
78    private object evaluationLock = new object();
79    private AutoResetEvent evaluationDone = new AutoResetEvent(false);
[6169]80    #endregion
81
82    #region Properties
[6140]83    public override System.Drawing.Image ItemImage {
84      get { return VSImageLibrary.Database; }
85    }
[6169]86    public int Size {
[6183]87      get {
88        lock (cacheLock) {
89          return index.Count;
90        }
91      }
[6169]92    }
[6183]93    public int ActiveEvaluations {
94      get {
95        lock (evaluationLock) {
96          return activeEvaluations.Count;
97        }
98      }
99    }
[6140]100    [Storable]
[6169]101    public int Hits { get; private set; }
[6140]102    #endregion
103
[6169]104    #region events
105    public event EventHandler SizeChanged;
106    public event EventHandler HitsChanged;
[6183]107    public event EventHandler ActiveEvalutionsChanged;
[6140]108
[6169]109    protected virtual void OnSizeChanged() {
110      EventHandler handler = SizeChanged;
[6140]111      if (handler != null)
112        handler(this, EventArgs.Empty);
113    }
[6169]114    protected virtual void OnHitsChanged() {
115      EventHandler handler = HitsChanged;
[6140]116      if (handler != null)
117        handler(this, EventArgs.Empty);
118    }
[6183]119    protected virtual void OnActiveEvalutionsChanged() {
120      EventHandler handler = ActiveEvalutionsChanged;
121      if (handler != null)
122        handler(this, EventArgs.Empty);
123    }
[6140]124    #endregion
125
[6169]126    #region Parameters
127    public FixedValueParameter<IntValue> CapacityParameter {
128      get { return (FixedValueParameter<IntValue>)Parameters["Capacity"]; }
129    }
[6183]130    public FixedValueParameter<BoolValue> PersistentCacheParameter {
131      get { return (FixedValueParameter<BoolValue>)Parameters["PersistentCache"]; }
132    }
[6169]133    #endregion
[6140]134
[6169]135    #region Parameter Values
136    public int Capacity {
137      get { return CapacityParameter.Value.Value; }
138      set { CapacityParameter.Value.Value = value; }
139    }
[6183]140    public bool IsPersistent {
141      get { return PersistentCacheParameter.Value.Value; }
142    }
[6169]143    #endregion
[6140]144
[6169]145    #region Persistence
146    [Storable(Name="Cache")]
147    private IEnumerable<KeyValuePair<string, double>> Cache_Persistence {
148      get {
[6183]149        if (IsPersistent) {
150          return GetCacheValues();
151        } else {
152          return Enumerable.Empty<KeyValuePair<string, double>>();
153        }
[6169]154      }
155      set {
[6183]156        SetCacheValues(value);
[6169]157      }
158    }
159    [StorableHook(HookType.AfterDeserialization)]
160    private void AfterDeserialization() {
161      RegisterEvents();
162    }
163    #endregion
164
[6140]165    #region Construction & Cloning
166    [StorableConstructor]
167    protected EvaluationCache(bool deserializing) : base(deserializing) { }
168    protected EvaluationCache(EvaluationCache original, Cloner cloner)
169      : base(original, cloner) {
[6183]170      SetCacheValues(original.GetCacheValues());
[6169]171      RegisterEvents();
[6140]172    }
173    public EvaluationCache() {
[6169]174      list = new LinkedList<CacheEntry>();
175      index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
176      Parameters.Add(new FixedValueParameter<IntValue>("Capacity", "Maximum number of cache entries.", new IntValue(10000)));
[6183]177      Parameters.Add(new FixedValueParameter<BoolValue>("PersistentCache", "Save cache when serializing object graph?", new BoolValue(false)));
[6169]178      RegisterEvents();
[6140]179    }
180    public override IDeepCloneable Clone(Cloner cloner) {
181      return new EvaluationCache(this, cloner);
182    }
183    #endregion
184
[6169]185    #region Event Handling
186    private void RegisterEvents() {
187      CapacityParameter.Value.ValueChanged += new EventHandler(Value_ValueChanged);
188    }
189
190    void Value_ValueChanged(object sender, EventArgs e) {
191      if (Capacity < 0)
192        throw new ArgumentOutOfRangeException("Cache capacity cannot be less than zero");
193      Trim();
194    }
195    #endregion
196
197    #region Methods
198    public void Reset() {
[6183]199      lock (cacheLock) {
200        list = new LinkedList<CacheEntry>();
201        index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
202        Hits = 0;
203      }
[6169]204      OnSizeChanged();
205      OnHitsChanged();
206    }
207
[6140]208    public double GetValue(SolutionMessage message, Evaluator evaluate) {
[6169]209      CacheEntry entry = new CacheEntry(message.ToString());
210      LinkedListNode<CacheEntry> node;
[6183]211      bool lockTaken = false;
212      try {
213        Monitor.Enter(cacheLock, ref lockTaken);
214        if (index.TryGetValue(entry, out node)) {
215          list.Remove(node);
216          list.AddLast(node);
217          Hits++;
218          lockTaken = false;
219          Monitor.Exit(cacheLock);
220          OnHitsChanged();
221          return node.Value.Value;
222        } else {
223          lockTaken = false;
224          Monitor.Exit(cacheLock);
225          return Evaluate(message, evaluate, entry);
226        }
227      } finally {
228        if (lockTaken)
229          Monitor.Exit(cacheLock);
[6140]230      }
231    }
232
[6183]233    private double Evaluate(SolutionMessage message, Evaluator evaluate, CacheEntry entry) {
234      bool lockTaken = false;
235      try {
236        Monitor.Enter(evaluationLock, ref lockTaken);
237        if (activeEvaluations.Contains(entry.Key)) {
238          while (activeEvaluations.Contains(entry.Key)) {
239            lockTaken = false;
240            Monitor.Exit(evaluationLock);
241            evaluationDone.WaitOne();
242            Monitor.Enter(evaluationLock, ref lockTaken);
243          }
244          lock (cacheLock) {
245            return index[entry].Value.Value;
246          }
247        } else {
248          activeEvaluations.Add(entry.Key);
249          lockTaken = false;
250          Monitor.Exit(evaluationLock);
251          OnActiveEvalutionsChanged();
[6188]252          try {
253            entry.Value = evaluate(message);
254            lock (cacheLock) {
255              index[entry] = list.AddLast(entry);
256            }
257            Trim();
258          } finally {
259            lock (evaluationLock) {
260              activeEvaluations.Remove(entry.Key);
261              evaluationDone.Set();
262            }
263            OnActiveEvalutionsChanged();
[6183]264          }
265          return entry.Value;
266        }
267      } finally {
268        if (lockTaken)
269          Monitor.Exit(evaluationLock);
270      }
271    }
272
[6169]273    private void Trim() {
[6183]274      lock (cacheLock) {
275        while (list.Count > Capacity) {
276          LinkedListNode<CacheEntry> item = list.First;
277          list.Remove(item);
278          index.Remove(item.Value);
279        }
[6169]280      }
281      OnSizeChanged();
[6140]282    }
[6183]283    private IEnumerable<KeyValuePair<string, double>> GetCacheValues() {
284      lock (cacheLock) {
285        return index.ToDictionary(kvp => kvp.Key.Key, kvp => kvp.Key.Value);
286      }
287    }
288    private void SetCacheValues(IEnumerable<KeyValuePair<string, double>> value) {
289      lock (cacheLock) {
290        list = new LinkedList<CacheEntry>();
291        index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
292        foreach (var kvp in value) {
293          var entry = new CacheEntry(kvp.Key);
294          entry.Value = kvp.Value;
295          index[entry] = list.AddLast(entry);
296        }
297      }
298    }
[6169]299    #endregion
300
[6140]301  }
302}
Note: See TracBrowser for help on using the repository browser.