001/* An actor that consumes a specified amount of real time.
002
003 Copyright (c) 2008-2018 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
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.actor.lib;
029
030import ptolemy.data.BooleanToken;
031import ptolemy.data.LongToken;
032import ptolemy.data.Token;
033import ptolemy.data.expr.Parameter;
034import ptolemy.data.type.BaseType;
035import ptolemy.kernel.CompositeEntity;
036import ptolemy.kernel.util.IllegalActionException;
037import ptolemy.kernel.util.NameDuplicationException;
038
039///////////////////////////////////////////////////////////////////
040//// ExecutionTime
041
042/**
043 Read the input token, if there is one, execute an (uninteresting)
044 computation to consume a specified amount of real time or to execute
045 it a fixed number of times, and produce
046 on the output the actual execution time (in milliseconds). Unlike the
047 Sleep actor, which suspends the calling thread for the specified
048 amount of time, this actor performs a computation during the
049 specified amount of time, thus consuming compute resources.
050 If <i>realTime</i> is true, then the number of computations it
051 performs is not fixed, but rather depends on what the thread
052 scheduler does. If it is false, then the amount of computation
053 done is fixed. The default is false.
054
055 @author Edward A. Lee
056 @version $Id$
057 @since Ptolemy II 8.0
058 @see Sleep
059 @Pt.ProposedRating Yellow (eal)
060 @Pt.AcceptedRating Red (cxh)
061 */
062public class ExecutionTime extends LimitedFiringSource {
063
064    /** Construct an actor with the given container and name.
065     *  @param container The container.
066     *  @param name The name of this actor.
067     *  @exception IllegalActionException If the actor cannot be contained
068     *   by the proposed container.
069     *  @exception NameDuplicationException If the container already has an
070     *   actor with this name.
071     */
072    public ExecutionTime(CompositeEntity container, String name)
073            throws NameDuplicationException, IllegalActionException {
074        super(container, name);
075        executionTime = new Parameter(this, "executionTime");
076        executionTime.setTypeEquals(BaseType.LONG);
077        executionTime.setExpression("1000L");
078
079        realTime = new Parameter(this, "realTime");
080        realTime.setTypeEquals(BaseType.BOOLEAN);
081        realTime.setExpression("false");
082
083        granularity = new Parameter(this, "granularity");
084        granularity.setTypeEquals(BaseType.LONG);
085        granularity.setExpression("400000L");
086
087        output.setTypeEquals(BaseType.LONG);
088
089        // Show the firingCountLimit parameter last.
090        firingCountLimit.moveToLast();
091    }
092
093    ///////////////////////////////////////////////////////////////////
094    ////                         parameters                        ////
095
096    /** The amount of time to consume. This is either in milliseconds,
097     *  if the realTime parameter is set to true, or in the number of
098     *  iterations of a fixed computation, if the realTime parameter
099     *  is set to false. This is a long
100     *  that defaults to 1000L.
101     */
102    public Parameter executionTime;
103
104    /** The granularity of the computation. This parameter specifies
105     *  the number of additions performed in each invocation of the
106     *  (uninteresting) computation. This is a long, which defaults
107     *  to 400000, which yields a computation time granularity
108     *  of approximately 1msec on a MacBook Pro.
109     */
110    public Parameter granularity;
111
112    /** If true, then the executionTime parameter is
113     *  interpreted as milliseconds. If it is false (the default), then the
114     *  executionTime parameter is interpreted to specify the number
115     *  of cycles of a fixed computation.  Use false to specify
116     *  a fixed computational load, and use true to specify an
117     *  amount of real time to consume. When this is true,
118     *  if the thread executing the fire() method is preempted
119     *  during its run, then less computation is done.
120     */
121    public Parameter realTime;
122
123    ///////////////////////////////////////////////////////////////////
124    ////                         public methods                    ////
125
126    /** Read the input token, consume time, the produce on the
127     *  output the actual execution time used.
128     *  @exception IllegalActionException If send() throws it.
129     */
130    @Override
131    public void fire() throws IllegalActionException {
132        long start = System.currentTimeMillis();
133        super.fire();
134        for (int i = 0; i < trigger.getWidth(); i++) {
135            if (trigger.hasToken(i)) {
136                // Read and discard the input.
137                trigger.get(i);
138            }
139        }
140        long executionTimeValue = ((LongToken) executionTime.getToken())
141                .longValue();
142        long granularityValue = ((LongToken) granularity.getToken())
143                .longValue();
144        boolean realTimeValue = ((BooleanToken) realTime.getToken())
145                .booleanValue();
146        boolean moreToDo = true;
147        long count = 0L;
148        int dummy = 0;
149        while (moreToDo) {
150            // NOTE: The number here determines the granularity.
151            for (int i = 0; i < granularityValue; i++) {
152                dummy++;
153            }
154            if (realTimeValue) {
155                moreToDo = System.currentTimeMillis()
156                        - start < executionTimeValue;
157            } else {
158                moreToDo = count < executionTimeValue;
159            }
160            count++;
161        }
162        // To ensure that an optimizer does not optimize it away, print the
163        // dummy variable.
164        System.out.println("Fired ExecutionTime: " + dummy);
165        // Produce on the output the actual time consumed, in case because
166        // of the granularity above it differs from the specified time.
167        Token result = new LongToken(System.currentTimeMillis() - start);
168        output.send(0, result);
169    }
170}