Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.Problems.ExternalEvaluation/3.4/EvaluationCache.cs @ 17181

Last change on this file since 17181 was 17181, checked in by swagner, 5 years ago

#2875: Merged r17180 from trunk to stable

File size: 11.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 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.Globalization;
28using System.IO;
29using System.Linq;
30using System.Text.RegularExpressions;
31using System.Threading;
32using Google.ProtocolBuffers;
33using HeuristicLab.Common;
34using HeuristicLab.Common.Resources;
35using HeuristicLab.Core;
36using HeuristicLab.Data;
37using HeuristicLab.Parameters;
38using HEAL.Attic;
39
40namespace HeuristicLab.Problems.ExternalEvaluation {
41
42  [Item("EvaluationCache", "Cache for external evaluation values")]
43  [StorableType("FDB10541-FA1C-4CF9-8175-83A55700C052")]
44  public class EvaluationCache : ParameterizedNamedItem {
45
46    #region Types
47    private sealed class CacheEntry {
48      public readonly string Key;
49      private QualityMessage message;
50      private byte[] rawMessage;
51
52      private object lockObject = new object();
53
54      public byte[] RawMessage {
55        get { return rawMessage; }
56        set {
57          lock (lockObject) {
58            rawMessage = value;
59            message = null;
60          }
61        }
62      }
63
64      public CacheEntry(string key) {
65        Key = key;
66      }
67
68      public QualityMessage GetMessage(ExtensionRegistry extensions) {
69        lock (lockObject) {
70          if (message == null && rawMessage != null)
71            message = QualityMessage.ParseFrom(ByteString.CopyFrom(rawMessage), extensions);
72        }
73        return message;
74      }
75      public void SetMessage(QualityMessage value) {
76        lock (lockObject) {
77          message = value;
78          rawMessage = value.ToByteArray();
79        }
80      }
81
82      public override bool Equals(object obj) {
83        CacheEntry other = obj as CacheEntry;
84        if (other == null)
85          return false;
86        return Key.Equals(other.Key);
87      }
88
89      public override int GetHashCode() {
90        return Key.GetHashCode();
91      }
92
93      public string QualityString(IFormatProvider formatProvider = null) {
94        if (formatProvider == null) formatProvider = CultureInfo.CurrentCulture;
95        if (RawMessage == null) return "-";
96        var msg = message ?? CreateBasicQualityMessage();
97        switch (msg.Type) {
98          case QualityMessage.Types.Type.SingleObjectiveQualityMessage:
99            return msg.GetExtension(SingleObjectiveQualityMessage.QualityMessage_).Quality.ToString(formatProvider);
100          case QualityMessage.Types.Type.MultiObjectiveQualityMessage:
101            var qualities = msg.GetExtension(MultiObjectiveQualityMessage.QualityMessage_).QualitiesList;
102            return string.Format("[{0}]", string.Join(",", qualities.Select(q => q.ToString(formatProvider))));
103          default:
104            return "-";
105        }
106      }
107      private QualityMessage CreateBasicQualityMessage() {
108        var extensions = ExtensionRegistry.CreateInstance();
109        ExternalEvaluationMessages.RegisterAllExtensions(extensions);
110        return QualityMessage.ParseFrom(ByteString.CopyFrom(rawMessage), extensions);
111      }
112
113      public override string ToString() {
114        return string.Format("{{{0} : {1}}}", Key, QualityString());
115      }
116    }
117
118    public delegate QualityMessage Evaluator(SolutionMessage message);
119    #endregion
120
121    #region Fields
122    private LinkedList<CacheEntry> list;
123    private Dictionary<CacheEntry, LinkedListNode<CacheEntry>> index;
124
125    private HashSet<string> activeEvaluations = new HashSet<string>();
126    private object cacheLock = new object();
127    #endregion
128
129    #region Properties
130    public static new System.Drawing.Image StaticItemImage {
131      get { return VSImageLibrary.Database; }
132    }
133    public int Size { get { lock (cacheLock) return index.Count; } }
134    public int ActiveEvaluations { get { lock (cacheLock) return activeEvaluations.Count; } }
135
136    [Storable]
137    public int Hits { get; private set; }
138    #endregion
139
140    #region events
141    public event EventHandler Changed;
142
143    protected virtual void OnChanged() {
144      EventHandler handler = Changed;
145      if (handler != null)
146        handler(this, EventArgs.Empty);
147    }
148    #endregion
149
150    #region Parameters
151    public FixedValueParameter<IntValue> CapacityParameter {
152      get { return (FixedValueParameter<IntValue>)Parameters["Capacity"]; }
153    }
154    public FixedValueParameter<BoolValue> PersistentCacheParameter {
155      get { return (FixedValueParameter<BoolValue>)Parameters["PersistentCache"]; }
156    }
157    #endregion
158
159    #region Parameter Values
160    public int Capacity {
161      get { return CapacityParameter.Value.Value; }
162      set { CapacityParameter.Value.Value = value; }
163    }
164    public bool IsPersistent {
165      get { return PersistentCacheParameter.Value.Value; }
166    }
167    #endregion
168
169    #region Persistence
170    #region BackwardsCompatibility3.4
171    [Storable(Name = "Cache")]
172    private IEnumerable<KeyValuePair<string, double>> Cache_Persistence_backwardscompatability {
173      get { return Enumerable.Empty<KeyValuePair<string, double>>(); }
174      set {
175        var rawMessages = value.ToDictionary(kvp => kvp.Key,
176          kvp => QualityMessage.CreateBuilder()
177            .SetSolutionId(0)
178            .SetExtension(
179              SingleObjectiveQualityMessage.QualityMessage_,
180              SingleObjectiveQualityMessage.CreateBuilder().SetQuality(kvp.Value).Build())
181            .Build().ToByteArray());
182        SetCacheValues(rawMessages);
183      }
184    }
185    #endregion
186    [Storable(Name = "CacheNew")]
187    private IEnumerable<KeyValuePair<string, byte[]>> Cache_Persistence {
188      get { return IsPersistent ? GetCacheValues() : Enumerable.Empty<KeyValuePair<string, byte[]>>(); }
189      set { SetCacheValues(value); }
190    }
191    [StorableHook(HookType.AfterDeserialization)]
192    private void AfterDeserialization() {
193      RegisterEvents();
194    }
195    #endregion
196
197    #region Construction & Cloning
198    [StorableConstructor]
199    protected EvaluationCache(StorableConstructorFlag _) : base(_) { }
200    protected EvaluationCache(EvaluationCache original, Cloner cloner)
201        : base(original, cloner) {
202      SetCacheValues(original.GetCacheValues());
203      Hits = original.Hits;
204      RegisterEvents();
205    }
206    public EvaluationCache() {
207      list = new LinkedList<CacheEntry>();
208      index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
209      Parameters.Add(new FixedValueParameter<IntValue>("Capacity", "Maximum number of cache entries.", new IntValue(10000)));
210      Parameters.Add(new FixedValueParameter<BoolValue>("PersistentCache", "Save cache when serializing object graph?", new BoolValue(false)));
211      RegisterEvents();
212    }
213    public override IDeepCloneable Clone(Cloner cloner) {
214      return new EvaluationCache(this, cloner);
215    }
216    #endregion
217
218    #region Event Handling
219    private void RegisterEvents() {
220      CapacityParameter.Value.ValueChanged += new EventHandler(CapacityChanged);
221    }
222
223    void CapacityChanged(object sender, EventArgs e) {
224      if (Capacity < 0)
225        throw new ArgumentOutOfRangeException("Cache capacity cannot be less than zero");
226      lock (cacheLock)
227        Trim();
228      OnChanged();
229    }
230    #endregion
231
232    #region Methods
233    public void Reset() {
234      lock (cacheLock) {
235        list = new LinkedList<CacheEntry>();
236        index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
237        Hits = 0;
238      }
239      OnChanged();
240    }
241
242    public QualityMessage GetValue(SolutionMessage message, Evaluator evaluate, ExtensionRegistry extensions) {
243      var entry = new CacheEntry(message.ToString());
244      bool lockTaken = false;
245      bool waited = false;
246      try {
247        Monitor.Enter(cacheLock, ref lockTaken);
248        while (true) {
249          LinkedListNode<CacheEntry> node;
250          if (index.TryGetValue(entry, out node)) {
251            list.Remove(node);
252            list.AddLast(node);
253            Hits++;
254            lockTaken = false;
255            Monitor.Exit(cacheLock);
256            OnChanged();
257            return node.Value.GetMessage(extensions);
258          } else {
259            if (!waited && activeEvaluations.Contains(entry.Key)) {
260              while (activeEvaluations.Contains(entry.Key))
261                Monitor.Wait(cacheLock);
262              waited = true;
263            } else {
264              activeEvaluations.Add(entry.Key);
265              lockTaken = false;
266              Monitor.Exit(cacheLock);
267              OnChanged();
268              try {
269                entry.SetMessage(evaluate(message));
270                Monitor.Enter(cacheLock, ref lockTaken);
271                index[entry] = list.AddLast(entry);
272                Trim();
273              } finally {
274                if (!lockTaken)
275                  Monitor.Enter(cacheLock, ref lockTaken);
276                activeEvaluations.Remove(entry.Key);
277                Monitor.PulseAll(cacheLock);
278                lockTaken = false;
279                Monitor.Exit(cacheLock);
280              }
281              OnChanged();
282              return entry.GetMessage(extensions);
283            }
284          }
285        }
286      } finally {
287        if (lockTaken)
288          Monitor.Exit(cacheLock);
289      }
290    }
291
292    private void Trim() {
293      while (list.Count > Capacity) {
294        var item = list.First;
295        list.Remove(item);
296        index.Remove(item.Value);
297      }
298    }
299
300    private IEnumerable<KeyValuePair<string, byte[]>> GetCacheValues() {
301      lock (cacheLock) {
302        return index.ToDictionary(kvp => kvp.Key.Key, kvp => kvp.Key.RawMessage);
303      }
304    }
305
306    private void SetCacheValues(IEnumerable<KeyValuePair<string, byte[]>> value) {
307      lock (cacheLock) {
308        if (list == null) list = new LinkedList<CacheEntry>();
309        if (index == null) index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
310        foreach (var kvp in value) {
311          var entry = new CacheEntry(kvp.Key) { RawMessage = kvp.Value };
312          index[entry] = list.AddLast(entry);
313        }
314      }
315    }
316
317    public void Save(string filename) {
318      using (var writer = new StreamWriter(filename)) {
319        lock (cacheLock) {
320          foreach (var entry in list) {
321            writer.WriteLine(string.Format(CultureInfo.InvariantCulture,
322              "\"{0}\", {1}",
323              Regex.Replace(entry.Key, "\\s", "").Replace("\"", "\"\""),
324              entry.QualityString(CultureInfo.InvariantCulture)));
325          }
326        }
327        writer.Close();
328      }
329    }
330    #endregion
331  }
332}
Note: See TracBrowser for help on using the repository browser.