Free cookie consent management tool by TermsFeed Policy Generator

source: branches/histogram/HeuristicLab.Problems.ExternalEvaluation/3.3/EvaluationCache.cs @ 6195

Last change on this file since 6195 was 6195, checked in by abeham, 13 years ago

#1465

  • updated branch with latest version of trunk
File size: 9.4 KB
Line 
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/>.
19 *
20 * The LRU cache is based on an idea by Robert Rossney see
21 * <http://csharp-lru-cache.googlecode.com>.
22 */
23#endregion
24
25using System;
26using System.Collections.Generic;
27using System.Linq;
28using System.Threading;
29using HeuristicLab.Common;
30using HeuristicLab.Common.Resources;
31using HeuristicLab.Core;
32using HeuristicLab.Data;
33using HeuristicLab.Parameters;
34using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
35namespace HeuristicLab.Problems.ExternalEvaluation {
36
37  [Item("EvaluationCache", "Cache for external evaluation values")]
38  [StorableClass]
39  public class EvaluationCache : ParameterizedNamedItem {
40
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
70    public delegate double Evaluator(SolutionMessage message);
71    #endregion
72
73    #region Fields
74    private LinkedList<CacheEntry> list;
75    private Dictionary<CacheEntry, LinkedListNode<CacheEntry>> index;
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);
80    #endregion
81
82    #region Properties
83    public override System.Drawing.Image ItemImage {
84      get { return VSImageLibrary.Database; }
85    }
86    public int Size {
87      get {
88        lock (cacheLock) {
89          return index.Count;
90        }
91      }
92    }
93    public int ActiveEvaluations {
94      get {
95        lock (evaluationLock) {
96          return activeEvaluations.Count;
97        }
98      }
99    }
100    [Storable]
101    public int Hits { get; private set; }
102    #endregion
103
104    #region events
105    public event EventHandler SizeChanged;
106    public event EventHandler HitsChanged;
107    public event EventHandler ActiveEvalutionsChanged;
108
109    protected virtual void OnSizeChanged() {
110      EventHandler handler = SizeChanged;
111      if (handler != null)
112        handler(this, EventArgs.Empty);
113    }
114    protected virtual void OnHitsChanged() {
115      EventHandler handler = HitsChanged;
116      if (handler != null)
117        handler(this, EventArgs.Empty);
118    }
119    protected virtual void OnActiveEvalutionsChanged() {
120      EventHandler handler = ActiveEvalutionsChanged;
121      if (handler != null)
122        handler(this, EventArgs.Empty);
123    }
124    #endregion
125
126    #region Parameters
127    public FixedValueParameter<IntValue> CapacityParameter {
128      get { return (FixedValueParameter<IntValue>)Parameters["Capacity"]; }
129    }
130    public FixedValueParameter<BoolValue> PersistentCacheParameter {
131      get { return (FixedValueParameter<BoolValue>)Parameters["PersistentCache"]; }
132    }
133    #endregion
134
135    #region Parameter Values
136    public int Capacity {
137      get { return CapacityParameter.Value.Value; }
138      set { CapacityParameter.Value.Value = value; }
139    }
140    public bool IsPersistent {
141      get { return PersistentCacheParameter.Value.Value; }
142    }
143    #endregion
144
145    #region Persistence
146    [Storable(Name="Cache")]
147    private IEnumerable<KeyValuePair<string, double>> Cache_Persistence {
148      get {
149        if (IsPersistent) {
150          return GetCacheValues();
151        } else {
152          return Enumerable.Empty<KeyValuePair<string, double>>();
153        }
154      }
155      set {
156        SetCacheValues(value);
157      }
158    }
159    [StorableHook(HookType.AfterDeserialization)]
160    private void AfterDeserialization() {
161      RegisterEvents();
162    }
163    #endregion
164
165    #region Construction & Cloning
166    [StorableConstructor]
167    protected EvaluationCache(bool deserializing) : base(deserializing) { }
168    protected EvaluationCache(EvaluationCache original, Cloner cloner)
169      : base(original, cloner) {
170      SetCacheValues(original.GetCacheValues());
171      RegisterEvents();
172    }
173    public EvaluationCache() {
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)));
177      Parameters.Add(new FixedValueParameter<BoolValue>("PersistentCache", "Save cache when serializing object graph?", new BoolValue(false)));
178      RegisterEvents();
179    }
180    public override IDeepCloneable Clone(Cloner cloner) {
181      return new EvaluationCache(this, cloner);
182    }
183    #endregion
184
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() {
199      lock (cacheLock) {
200        list = new LinkedList<CacheEntry>();
201        index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
202        Hits = 0;
203      }
204      OnSizeChanged();
205      OnHitsChanged();
206    }
207
208    public double GetValue(SolutionMessage message, Evaluator evaluate) {
209      CacheEntry entry = new CacheEntry(message.ToString());
210      LinkedListNode<CacheEntry> node;
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);
230      }
231    }
232
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();
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();
264          }
265          return entry.Value;
266        }
267      } finally {
268        if (lockTaken)
269          Monitor.Exit(evaluationLock);
270      }
271    }
272
273    private void Trim() {
274      lock (cacheLock) {
275        while (list.Count > Capacity) {
276          LinkedListNode<CacheEntry> item = list.First;
277          list.Remove(item);
278          index.Remove(item.Value);
279        }
280      }
281      OnSizeChanged();
282    }
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    }
299    #endregion
300
301  }
302}
Note: See TracBrowser for help on using the repository browser.