Free cookie consent management tool by TermsFeed Policy Generator

source: branches/WebJobManager/HeuristicLab.Problems.ExternalEvaluation/3.4/EvaluationCache.cs @ 13656

Last change on this file since 13656 was 13656, checked in by ascheibe, 8 years ago

#2582 created branch for Hive Web Job Manager

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