Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 8010 was 7737, checked in by epitzer, 13 years ago

#1722 add missing Hits property in cloning constructor and clean up code

File size: 8.8 KB
RevLine 
[6140]1#region License Information
2/* HeuristicLab
[7259]3 * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[6140]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;
[6519]27using System.Globalization;
28using System.IO;
[6140]29using System.Linq;
[6519]30using System.Text.RegularExpressions;
[6183]31using System.Threading;
[6140]32using HeuristicLab.Common;
33using HeuristicLab.Common.Resources;
34using HeuristicLab.Core;
[6169]35using HeuristicLab.Data;
36using HeuristicLab.Parameters;
[6140]37using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
[6519]38
[6140]39namespace HeuristicLab.Problems.ExternalEvaluation {
40
41  [Item("EvaluationCache", "Cache for external evaluation values")]
42  [StorableClass]
[6169]43  public class EvaluationCache : ParameterizedNamedItem {
[6140]44
[6169]45    #region Types
[6291]46    private sealed class CacheEntry {
[6169]47
[6291]48      public readonly string Key;
49      public double Value;
[6169]50
51      public CacheEntry(string key, double value) {
52        Key = key;
53        Value = value;
54      }
55
[6291]56      public CacheEntry(string key) {
57        Key = key;
58      }
[6169]59
60      public override bool Equals(object obj) {
61        CacheEntry other = obj as CacheEntry;
62        if (other == null)
63          return false;
64        return Key.Equals(other.Key);
65      }
66
67      public override int GetHashCode() {
68        return Key.GetHashCode();
69      }
70
71      public override string ToString() {
72        return string.Format("{{{0} : {1}}}", Key, Value);
73      }
74    }
75
[6140]76    public delegate double Evaluator(SolutionMessage message);
[6169]77    #endregion
[6140]78
[6169]79    #region Fields
80    private LinkedList<CacheEntry> list;
81    private Dictionary<CacheEntry, LinkedListNode<CacheEntry>> index;
[6291]82
[6188]83    private HashSet<string> activeEvaluations = new HashSet<string>();
[6519]84    private object cacheLock = new object();
[6169]85    #endregion
86
87    #region Properties
[7201]88    public static new System.Drawing.Image StaticItemImage {
[6140]89      get { return VSImageLibrary.Database; }
90    }
[6291]91    public int Size { get { lock (cacheLock) return index.Count; } }
92    public int ActiveEvaluations { get { lock (cacheLock) return activeEvaluations.Count; } }
93
[6140]94    [Storable]
[6169]95    public int Hits { get; private set; }
[6140]96    #endregion
97
[6169]98    #region events
[6291]99    public event EventHandler Changed;
[6140]100
[6291]101    protected virtual void OnChanged() {
102      EventHandler handler = Changed;
[6140]103      if (handler != null)
104        handler(this, EventArgs.Empty);
105    }
106    #endregion
107
[6169]108    #region Parameters
109    public FixedValueParameter<IntValue> CapacityParameter {
110      get { return (FixedValueParameter<IntValue>)Parameters["Capacity"]; }
111    }
[6183]112    public FixedValueParameter<BoolValue> PersistentCacheParameter {
113      get { return (FixedValueParameter<BoolValue>)Parameters["PersistentCache"]; }
114    }
[6169]115    #endregion
[6140]116
[6169]117    #region Parameter Values
118    public int Capacity {
119      get { return CapacityParameter.Value.Value; }
120      set { CapacityParameter.Value.Value = value; }
121    }
[6183]122    public bool IsPersistent {
123      get { return PersistentCacheParameter.Value.Value; }
124    }
[6169]125    #endregion
[6140]126
[6169]127    #region Persistence
[6519]128    [Storable(Name = "Cache")]
[6169]129    private IEnumerable<KeyValuePair<string, double>> Cache_Persistence {
130      get {
[6183]131        if (IsPersistent) {
132          return GetCacheValues();
133        } else {
134          return Enumerable.Empty<KeyValuePair<string, double>>();
135        }
[6169]136      }
137      set {
[6183]138        SetCacheValues(value);
[6169]139      }
140    }
141    [StorableHook(HookType.AfterDeserialization)]
142    private void AfterDeserialization() {
143      RegisterEvents();
144    }
145    #endregion
146
[6140]147    #region Construction & Cloning
148    [StorableConstructor]
149    protected EvaluationCache(bool deserializing) : base(deserializing) { }
150    protected EvaluationCache(EvaluationCache original, Cloner cloner)
151      : base(original, cloner) {
[6183]152      SetCacheValues(original.GetCacheValues());
[7737]153      Hits = original.Hits;
[6169]154      RegisterEvents();
[6140]155    }
156    public EvaluationCache() {
[6169]157      list = new LinkedList<CacheEntry>();
158      index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
159      Parameters.Add(new FixedValueParameter<IntValue>("Capacity", "Maximum number of cache entries.", new IntValue(10000)));
[6183]160      Parameters.Add(new FixedValueParameter<BoolValue>("PersistentCache", "Save cache when serializing object graph?", new BoolValue(false)));
[6169]161      RegisterEvents();
[6140]162    }
163    public override IDeepCloneable Clone(Cloner cloner) {
164      return new EvaluationCache(this, cloner);
165    }
166    #endregion
167
[6169]168    #region Event Handling
169    private void RegisterEvents() {
[7737]170      CapacityParameter.Value.ValueChanged += new EventHandler(CapacityChanged);
[6169]171    }
172
[7737]173    void CapacityChanged(object sender, EventArgs e) {
[6169]174      if (Capacity < 0)
175        throw new ArgumentOutOfRangeException("Cache capacity cannot be less than zero");
[6291]176      lock (cacheLock)
177        Trim();
178      OnChanged();
[6169]179    }
180    #endregion
181
182    #region Methods
183    public void Reset() {
[6183]184      lock (cacheLock) {
185        list = new LinkedList<CacheEntry>();
186        index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
187        Hits = 0;
188      }
[6291]189      OnChanged();
[6169]190    }
191
[6140]192    public double GetValue(SolutionMessage message, Evaluator evaluate) {
[7737]193      var entry = new CacheEntry(message.ToString());
[6183]194      bool lockTaken = false;
[6291]195      bool waited = false;
[6519]196      try {
[6183]197        Monitor.Enter(cacheLock, ref lockTaken);
[6291]198        while (true) {
[7737]199          LinkedListNode<CacheEntry> node;
[6291]200          if (index.TryGetValue(entry, out node)) {
201            list.Remove(node);
202            list.AddLast(node);
203            Hits++;
[6183]204            lockTaken = false;
[6291]205            Monitor.Exit(cacheLock);
206            OnChanged();
207            return node.Value.Value;
208          } else {
209            if (!waited && activeEvaluations.Contains(entry.Key)) {
210              while (activeEvaluations.Contains(entry.Key))
211                Monitor.Wait(cacheLock);
212              waited = true;
213            } else {
214              activeEvaluations.Add(entry.Key);
215              lockTaken = false;
216              Monitor.Exit(cacheLock);
217              OnChanged();
218              try {
219                entry.Value = evaluate(message);
220                Monitor.Enter(cacheLock, ref lockTaken);
221                index[entry] = list.AddLast(entry);
222                Trim();
[7201]223              }
224              finally {
[6291]225                if (!lockTaken)
226                  Monitor.Enter(cacheLock, ref lockTaken);
227                activeEvaluations.Remove(entry.Key);
228                Monitor.PulseAll(cacheLock);
229                lockTaken = false;
230                Monitor.Exit(cacheLock);
231              }
232              OnChanged();
233              return entry.Value;
[6188]234            }
[6183]235          }
236        }
[7201]237      }
238      finally {
[6183]239        if (lockTaken)
[6291]240          Monitor.Exit(cacheLock);
[6183]241      }
242    }
243
[6169]244    private void Trim() {
[6291]245      while (list.Count > Capacity) {
[7737]246        var item = list.First;
[6291]247        list.Remove(item);
248        index.Remove(item.Value);
[6169]249      }
[6140]250    }
[6265]251
[6183]252    private IEnumerable<KeyValuePair<string, double>> GetCacheValues() {
253      lock (cacheLock) {
254        return index.ToDictionary(kvp => kvp.Key.Key, kvp => kvp.Key.Value);
255      }
256    }
[6265]257
[6183]258    private void SetCacheValues(IEnumerable<KeyValuePair<string, double>> value) {
259      lock (cacheLock) {
260        list = new LinkedList<CacheEntry>();
261        index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
262        foreach (var kvp in value) {
[7737]263          var entry = new CacheEntry(kvp.Key) { Value = kvp.Value };
[6183]264          index[entry] = list.AddLast(entry);
265        }
266      }
267    }
[6265]268
269    public void Save(string filename) {
270      using (var writer = new StreamWriter(filename)) {
271        lock (cacheLock) {
272          foreach (var entry in list) {
273            writer.WriteLine(string.Format(CultureInfo.InvariantCulture,
274              "\"{0}\", {1}",
275              Regex.Replace(entry.Key, "\\s", "").Replace("\"", "\"\""),
276              entry.Value));
277          }
278        }
279        writer.Close();
280      }
281    }
[6169]282    #endregion
[6291]283  }
[6140]284}
Note: See TracBrowser for help on using the repository browser.