Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Problems.ExternalEvaluation/3.4/EvaluationCache.cs @ 16703

Last change on this file since 16703 was 16565, checked in by gkronber, 6 years ago

#2520: merged changes from PersistenceOverhaul branch (r16451:16564) into trunk

File size: 11.3 KB
RevLine 
[6140]1#region License Information
2/* HeuristicLab
[16565]3 * Copyright (C) 2002-2019 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;
[13180]32using Google.ProtocolBuffers;
[6140]33using HeuristicLab.Common;
34using HeuristicLab.Common.Resources;
35using HeuristicLab.Core;
[6169]36using HeuristicLab.Data;
37using HeuristicLab.Parameters;
[16565]38using HEAL.Attic;
[6519]39
[6140]40namespace HeuristicLab.Problems.ExternalEvaluation {
41
42  [Item("EvaluationCache", "Cache for external evaluation values")]
[16565]43  [StorableType("FDB10541-FA1C-4CF9-8175-83A55700C052")]
[6169]44  public class EvaluationCache : ParameterizedNamedItem {
[6140]45
[6169]46    #region Types
[6291]47    private sealed class CacheEntry {
48      public readonly string Key;
[13180]49      private QualityMessage message;
50      private byte[] rawMessage;
51
[13203]52      private object lockObject = new object();
53
[13180]54      public byte[] RawMessage {
55        get { return rawMessage; }
56        set {
[13203]57          lock (lockObject) {
58            rawMessage = value;
59            message = null;
60          }
[13180]61        }
[6169]62      }
63
[6291]64      public CacheEntry(string key) {
65        Key = key;
66      }
[6169]67
[13180]68      public QualityMessage GetMessage(ExtensionRegistry extensions) {
[13203]69        lock (lockObject) {
70          if (message == null && rawMessage != null)
71            message = QualityMessage.ParseFrom(ByteString.CopyFrom(rawMessage), extensions);
72        }
[13180]73        return message;
74      }
75      public void SetMessage(QualityMessage value) {
[13203]76        lock (lockObject) {
77          message = value;
78          rawMessage = value.ToByteArray();
79        }
[13180]80      }
81
[6169]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
[13180]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
[6169]113      public override string ToString() {
[13180]114        return string.Format("{{{0} : {1}}}", Key, QualityString());
[6169]115      }
116    }
117
[13180]118    public delegate QualityMessage Evaluator(SolutionMessage message);
[6169]119    #endregion
[6140]120
[6169]121    #region Fields
122    private LinkedList<CacheEntry> list;
123    private Dictionary<CacheEntry, LinkedListNode<CacheEntry>> index;
[6291]124
[6188]125    private HashSet<string> activeEvaluations = new HashSet<string>();
[6519]126    private object cacheLock = new object();
[6169]127    #endregion
128
129    #region Properties
[7201]130    public static new System.Drawing.Image StaticItemImage {
[6140]131      get { return VSImageLibrary.Database; }
132    }
[6291]133    public int Size { get { lock (cacheLock) return index.Count; } }
134    public int ActiveEvaluations { get { lock (cacheLock) return activeEvaluations.Count; } }
135
[6140]136    [Storable]
[6169]137    public int Hits { get; private set; }
[6140]138    #endregion
139
[6169]140    #region events
[6291]141    public event EventHandler Changed;
[6140]142
[6291]143    protected virtual void OnChanged() {
144      EventHandler handler = Changed;
[6140]145      if (handler != null)
146        handler(this, EventArgs.Empty);
147    }
148    #endregion
149
[6169]150    #region Parameters
151    public FixedValueParameter<IntValue> CapacityParameter {
152      get { return (FixedValueParameter<IntValue>)Parameters["Capacity"]; }
153    }
[6183]154    public FixedValueParameter<BoolValue> PersistentCacheParameter {
155      get { return (FixedValueParameter<BoolValue>)Parameters["PersistentCache"]; }
156    }
[6169]157    #endregion
[6140]158
[6169]159    #region Parameter Values
160    public int Capacity {
161      get { return CapacityParameter.Value.Value; }
162      set { CapacityParameter.Value.Value = value; }
163    }
[6183]164    public bool IsPersistent {
165      get { return PersistentCacheParameter.Value.Value; }
166    }
[6169]167    #endregion
[6140]168
[6169]169    #region Persistence
[13180]170    #region BackwardsCompatibility3.4
[6519]171    [Storable(Name = "Cache")]
[13203]172    private IEnumerable<KeyValuePair<string, double>> Cache_Persistence_backwardscompatability {
[13180]173      get { return Enumerable.Empty<KeyValuePair<string, double>>(); }
[6169]174      set {
[13180]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);
[6169]183      }
184    }
[13180]185    #endregion
186    [Storable(Name = "CacheNew")]
[13203]187    private IEnumerable<KeyValuePair<string, byte[]>> Cache_Persistence {
[13180]188      get { return IsPersistent ? GetCacheValues() : Enumerable.Empty<KeyValuePair<string, byte[]>>(); }
189      set { SetCacheValues(value); }
190    }
[6169]191    [StorableHook(HookType.AfterDeserialization)]
192    private void AfterDeserialization() {
193      RegisterEvents();
194    }
195    #endregion
196
[6140]197    #region Construction & Cloning
198    [StorableConstructor]
[16565]199    protected EvaluationCache(StorableConstructorFlag _) : base(_) { }
[6140]200    protected EvaluationCache(EvaluationCache original, Cloner cloner)
[13180]201        : base(original, cloner) {
[6183]202      SetCacheValues(original.GetCacheValues());
[7737]203      Hits = original.Hits;
[6169]204      RegisterEvents();
[6140]205    }
206    public EvaluationCache() {
[6169]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)));
[6183]210      Parameters.Add(new FixedValueParameter<BoolValue>("PersistentCache", "Save cache when serializing object graph?", new BoolValue(false)));
[6169]211      RegisterEvents();
[6140]212    }
213    public override IDeepCloneable Clone(Cloner cloner) {
214      return new EvaluationCache(this, cloner);
215    }
216    #endregion
217
[6169]218    #region Event Handling
219    private void RegisterEvents() {
[7737]220      CapacityParameter.Value.ValueChanged += new EventHandler(CapacityChanged);
[6169]221    }
222
[7737]223    void CapacityChanged(object sender, EventArgs e) {
[6169]224      if (Capacity < 0)
225        throw new ArgumentOutOfRangeException("Cache capacity cannot be less than zero");
[6291]226      lock (cacheLock)
227        Trim();
228      OnChanged();
[6169]229    }
230    #endregion
231
232    #region Methods
233    public void Reset() {
[6183]234      lock (cacheLock) {
235        list = new LinkedList<CacheEntry>();
236        index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
237        Hits = 0;
238      }
[6291]239      OnChanged();
[6169]240    }
241
[13180]242    public QualityMessage GetValue(SolutionMessage message, Evaluator evaluate, ExtensionRegistry extensions) {
[7737]243      var entry = new CacheEntry(message.ToString());
[6183]244      bool lockTaken = false;
[6291]245      bool waited = false;
[6519]246      try {
[6183]247        Monitor.Enter(cacheLock, ref lockTaken);
[6291]248        while (true) {
[7737]249          LinkedListNode<CacheEntry> node;
[6291]250          if (index.TryGetValue(entry, out node)) {
251            list.Remove(node);
252            list.AddLast(node);
253            Hits++;
[6183]254            lockTaken = false;
[6291]255            Monitor.Exit(cacheLock);
256            OnChanged();
[13180]257            return node.Value.GetMessage(extensions);
[6291]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 {
[13180]269                entry.SetMessage(evaluate(message));
[6291]270                Monitor.Enter(cacheLock, ref lockTaken);
271                index[entry] = list.AddLast(entry);
272                Trim();
[13180]273              } finally {
[6291]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();
[13180]282              return entry.GetMessage(extensions);
[6188]283            }
[6183]284          }
285        }
[13180]286      } finally {
[6183]287        if (lockTaken)
[6291]288          Monitor.Exit(cacheLock);
[6183]289      }
290    }
291
[6169]292    private void Trim() {
[6291]293      while (list.Count > Capacity) {
[7737]294        var item = list.First;
[6291]295        list.Remove(item);
296        index.Remove(item.Value);
[6169]297      }
[6140]298    }
[6265]299
[13180]300    private IEnumerable<KeyValuePair<string, byte[]>> GetCacheValues() {
[6183]301      lock (cacheLock) {
[13180]302        return index.ToDictionary(kvp => kvp.Key.Key, kvp => kvp.Key.RawMessage);
[6183]303      }
304    }
[6265]305
[13180]306    private void SetCacheValues(IEnumerable<KeyValuePair<string, byte[]>> value) {
[6183]307      lock (cacheLock) {
[13180]308        if (list == null) list = new LinkedList<CacheEntry>();
309        if (index == null) index = new Dictionary<CacheEntry, LinkedListNode<CacheEntry>>();
[6183]310        foreach (var kvp in value) {
[13180]311          var entry = new CacheEntry(kvp.Key) { RawMessage = kvp.Value };
[6183]312          index[entry] = list.AddLast(entry);
313        }
314      }
315    }
[6265]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("\"", "\"\""),
[13180]324              entry.QualityString(CultureInfo.InvariantCulture)));
[6265]325          }
326        }
327        writer.Close();
328      }
329    }
[6169]330    #endregion
[6291]331  }
[6140]332}
Note: See TracBrowser for help on using the repository browser.