001/* A extension of the SDFScheduler that caches the schedules.
002
003 Copyright (c) 2006-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023 PT_COPYRIGHT_VERSION_2
024 COPYRIGHTENDKEY
025
026 */
027package ptolemy.domains.sdf.kernel;
028
029import java.util.ArrayList;
030import java.util.HashMap;
031import java.util.Iterator;
032import java.util.LinkedList;
033import java.util.List;
034import java.util.Map;
035import java.util.TreeMap;
036
037import ptolemy.actor.Actor;
038import ptolemy.actor.CompositeActor;
039import ptolemy.actor.Director;
040import ptolemy.actor.IOPort;
041import ptolemy.actor.sched.NotSchedulableException;
042import ptolemy.actor.sched.Schedule;
043import ptolemy.actor.util.DFUtilities;
044import ptolemy.kernel.util.IllegalActionException;
045import ptolemy.kernel.util.NameDuplicationException;
046import ptolemy.kernel.util.Workspace;
047
048///////////////////////////////////////////////////////////////////
049//// CachedSDFScheduler
050
051/**
052 The CachedSDFScheduler extends the SDFScheduler by caching schedules.
053 The cached schedules are labeled by their corresponding rate signatures,
054 with the most recently used at the beginning of the cache queue. If the
055 rate signatures are contained in the cache keys, then the corresponding
056 schedule in the cache is used. Therefore, we do not need to recompute the
057 schedule again.
058 <p>
059 The size of the cache in the CachedSDFScheduler is usually set by its
060 containing director when constructing this scheduler. If the cache is
061 full, the least recently used schedule (at the end of the cache) is
062 discarded.
063
064 @see SDFScheduler
065
066 @author Ye Zhou. Contributor: Brian K. Vogel
067 @version $Id$
068 @since Ptolemy II 5.2
069 @Pt.ProposedRating Red (zhouye)
070 @Pt.AcceptedRating Red (cxh)
071 */
072public class CachedSDFScheduler extends SDFScheduler {
073    /** Construct a scheduler with no container(director)
074     *  in the default workspace, the name of the scheduler is
075     *  "Scheduler". The cache size is the default value of 0.
076     */
077    public CachedSDFScheduler() {
078        super();
079        constructCaches(0);
080    }
081
082    /** Construct a scheduler in the given workspace with the name
083     *  "Scheduler". The cache size is the default value of 0.
084     *  If the workspace argument is null, use the default workspace.
085     *  The scheduler is added to the list of objects in the workspace.
086     *  Increment the version number of the workspace.
087     *
088     *  @param workspace Object for synchronization and version tracking.
089     */
090    public CachedSDFScheduler(Workspace workspace) {
091        super(workspace);
092        constructCaches(0);
093    }
094
095    /** Construct a scheduler in the given container with the given name
096     *  and given cache size. The container argument must not be null, or a
097     *  NullPointerException will be thrown. This attribute will use the
098     *  workspace of the container for synchronization and version counts.
099     *  If the name argument is null, then the name is set to the empty string.
100     *  Increment the version of the workspace.
101     *  @param container The container.
102     *  @param name The name of this attribute.
103     *  @param cacheSize The cache size of this scheduler.
104     *  @exception IllegalActionException If the super class throws it.
105     *  @exception NameDuplicationException If the super class throws it.
106     */
107    public CachedSDFScheduler(Director container, String name, int cacheSize)
108            throws IllegalActionException, NameDuplicationException {
109        super(container, name);
110        constructCaches(cacheSize);
111    }
112
113    ///////////////////////////////////////////////////////////////////
114    ////                         public methods                    ////
115
116    /** Clear the schedule cache, cache keys and cache for external rates
117     *  of this scheduler.
118     */
119    public void clearCaches() {
120        if (_cacheSize > 0) {
121            _scheduleKeyList.clear();
122        }
123
124        _scheduleCache.clear();
125        _externalRatesCache.clear();
126        _mostRecentRates = "";
127    }
128
129    /** Construct the caches of this scheduler with the given cache size.
130     *  @param cacheSize The given cache sie.
131     */
132    public void constructCaches(int cacheSize) {
133        _scheduleCache = new HashMap();
134
135        if (cacheSize > 0) {
136            _scheduleKeyList = new ArrayList(cacheSize);
137        }
138
139        _externalRatesCache = new TreeMap();
140        _cacheSize = cacheSize;
141    }
142
143    ///////////////////////////////////////////////////////////////////
144    ////                         protected methods                 ////
145
146    /** Return the scheduling sequence. If the schedule exist in the
147     *  cache (schedules are identified by the rate signatures of ports),
148     *  then return the corresponding schedule in the cache. Otherwise,
149     *  compute the schedule and return it.
150     *  @return A schedule of the deeply contained opaque entities
151     *  in the firing order.
152     *  @exception NotSchedulableException If the super class throws it.
153     *  @exception IllegalActionException If the super class throws it.
154     */
155    @Override
156    protected Schedule _getSchedule()
157            throws NotSchedulableException, IllegalActionException {
158        Schedule schedule;
159
160        if (_inputPortList == null
161                || _workspaceVersion != workspace().getVersion()) {
162            _inputPortList = _getInputPortList();
163        }
164
165        if (_outputPortList == null
166                || _workspaceVersion != workspace().getVersion()) {
167            _outputPortList = _getOutputPortList();
168        }
169
170        _workspaceVersion = workspace().getVersion();
171
172        StringBuffer rates = new StringBuffer();
173
174        Iterator inputPorts = _inputPortList.iterator();
175
176        while (inputPorts.hasNext()) {
177            IOPort inputPort = (IOPort) inputPorts.next();
178            int rate = DFUtilities.getTokenConsumptionRate(inputPort);
179            rates.append(rate);
180
181            int initRate = DFUtilities.getTokenInitConsumption(inputPort);
182            rates.append("_");
183            rates.append(initRate);
184        }
185
186        Iterator outputPorts = _outputPortList.iterator();
187
188        while (outputPorts.hasNext()) {
189            IOPort outputPort = (IOPort) outputPorts.next();
190            int rate = DFUtilities.getTokenProductionRate(outputPort);
191            rates.append(rate);
192
193            int initRate = DFUtilities.getTokenInitProduction(outputPort);
194            rates.append("_");
195            rates.append(initRate);
196        }
197
198        String rateKey = rates.toString();
199
200        if (_scheduleCache.containsKey(rateKey)) {
201            // cache hit.
202            schedule = (Schedule) _scheduleCache.get(rateKey);
203
204            if (!rateKey.equals(_mostRecentRates)) {
205                _mostRecentRates = rateKey;
206
207                if (_cacheSize > 0) {
208                    // Remove the key from its old position in
209                    // the list and add it to the head of the list.
210                    _scheduleKeyList.remove(rateKey);
211                    _scheduleKeyList.add(0, rateKey);
212                }
213
214                Map externalRates = (Map) _externalRatesCache.get(rateKey);
215                _saveContainerRates(externalRates);
216            }
217        } else {
218            // cache miss.
219            _mostRecentRates = rateKey;
220
221            if (_cacheSize > 0) {
222                while (_scheduleKeyList.size() >= _cacheSize) {
223                    // Cache is full. Remove the end of the caches.
224                    Object key = _scheduleKeyList.get(_cacheSize - 1);
225                    _scheduleKeyList.remove(_cacheSize - 1);
226                    _scheduleCache.remove(key);
227                    _externalRatesCache.remove(key);
228                }
229
230                // Add key to head of list.
231                _scheduleKeyList.add(0, rateKey);
232            }
233
234            // Compute the SDF schedule.
235            schedule = super._getSchedule();
236
237            // Add key/schedule to the schedule map.
238            _scheduleCache.put(rateKey, schedule);
239
240            // Add external rates map to the external rates cache.
241            Map externalRates = getExternalRates();
242            _externalRatesCache.put(rateKey, externalRates);
243
244            // Note: we do not need to set the external rates of
245            // the container here. When the SDFSchedule is recomputed,
246            // it will set the external rates.
247        }
248
249        setValid(true);
250        return schedule;
251    }
252
253    ///////////////////////////////////////////////////////////////////
254    ////                         private methods                   ////
255
256    /** Return a list of all the input ports contained by the
257     *  deeply contained entities of the container of this director.
258     *  @return The list of input ports.
259     */
260    private List _getInputPortList() {
261        CompositeActor container = (CompositeActor) getContainer()
262                .getContainer();
263        List actors = container.deepEntityList();
264        Iterator actorIterator = actors.iterator();
265        List inputPortList = new LinkedList();
266
267        while (actorIterator.hasNext()) {
268            Actor containedActor = (Actor) actorIterator.next();
269            List temporaryInputPortList = containedActor.inputPortList();
270            Iterator inputPortIterator = temporaryInputPortList.iterator();
271
272            while (inputPortIterator.hasNext()) {
273                IOPort inputPort = (IOPort) inputPortIterator.next();
274                inputPortList.add(inputPort);
275            }
276        }
277
278        return inputPortList;
279    }
280
281    /** Return a list of all the output ports contained by the
282     *  deeply contained entities of the container of this director.
283     *  @return The list of output ports.
284     */
285    private List _getOutputPortList() {
286        CompositeActor container = (CompositeActor) getContainer()
287                .getContainer();
288        List actors = container.deepEntityList();
289        Iterator actorIterator2 = actors.iterator();
290        List outputPortList = new LinkedList();
291
292        while (actorIterator2.hasNext()) {
293            Actor containedActor = (Actor) actorIterator2.next();
294            List temporaryOutputPortList = containedActor.outputPortList();
295            Iterator outputPortIterator = temporaryOutputPortList.iterator();
296
297            while (outputPortIterator.hasNext()) {
298                IOPort outputPort = (IOPort) outputPortIterator.next();
299                outputPortList.add(outputPort);
300            }
301        }
302
303        return outputPortList;
304    }
305
306    ///////////////////////////////////////////////////////////////////
307    ////                         private variables                 ////
308    // The size of the cache.
309    private int _cacheSize;
310
311    // Map for the schedule cache.
312    private Map _scheduleCache;
313
314    // List of the schedule keys, which are strings that represent
315    // the rate signature.
316    private List _scheduleKeyList;
317
318    // Map for the cache of the external rates.
319    private Map _externalRatesCache;
320
321    // A string that represents the most recent port rates.
322    private String _mostRecentRates;
323
324    // A list of the input ports.
325    private List _inputPortList;
326
327    // A list of the output ports.
328    private List _outputPortList;
329
330    // Local workspace version
331    private long _workspaceVersion = 0;
332}