View Javadoc
1   //******************************************************************************
2   //
3   // File:    GuidedLongSchedule.java
4   // Package: edu.rit.pj
5   // Unit:    Class edu.rit.pj.GuidedLongSchedule
6   //
7   // This Java source file is copyright (C) 2009 by Alan Kaminsky. All rights
8   // reserved. For further information, contact the author, Alan Kaminsky, at
9   // ark@cs.rit.edu.
10  //
11  // This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
12  // software; you can redistribute it and/or modify it under the terms of the GNU
13  // General Public License as published by the Free Software Foundation; either
14  // version 3 of the License, or (at your option) any later version.
15  //
16  // PJ is distributed in the hope that it will be useful, but WITHOUT ANY
17  // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18  // A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19  //
20  // Linking this library statically or dynamically with other modules is making a
21  // combined work based on this library. Thus, the terms and conditions of the GNU
22  // General Public License cover the whole combination.
23  //
24  // As a special exception, the copyright holders of this library give you
25  // permission to link this library with independent modules to produce an
26  // executable, regardless of the license terms of these independent modules, and
27  // to copy and distribute the resulting executable under terms of your choice,
28  // provided that you also meet, for each linked independent module, the terms
29  // and conditions of the license of that module. An independent module is a module
30  // which is not derived from or based on this library. If you modify this library,
31  // you may extend this exception to your version of the library, but you are not
32  // obligated to do so. If you do not wish to do so, delete this exception
33  // statement from your version.
34  //
35  // A copy of the GNU General Public License is provided in the file gpl.txt. You
36  // may also obtain a copy of the GNU General Public License on the World Wide
37  // Web at http://www.gnu.org/licenses/gpl.html.
38  //
39  //******************************************************************************
40  package edu.rit.pj;
41  
42  import java.util.concurrent.atomic.AtomicLong;
43  
44  import edu.rit.util.LongRange;
45  
46  /**
47   * Class GuidedLongSchedule provides a self-guided schedule object. The loop
48   * index is type <code>long</code>. The loop iterations are apportioned into chunks
49   * of exponentially diminishing sizes. Each successive chunk's size is half the
50   * remaining number of iterations divided by the number of threads in the
51   * parallel team. However, each chunk is at least a given minimum size (a given
52   * minimum number of iterations per chunk). Each parallel team thread repeatedly
53   * performs the next available chunk of iterations until there are no more
54   * chunks. The final chunk may be smaller than the given minimum chunk size.
55   *
56   * @author Alan Kaminsky
57   * @version 18-Nov-2009
58   */
59  class GuidedLongSchedule
60          extends LongSchedule {
61  
62  // Hidden data members.
63      // Twice the number of parallel team threads.
64      private int two_K;
65  
66      // Loop iteration range.
67      private LongRange myLoopRange;
68  
69      // Number of iterations.
70      private long myLoopRangeLength;
71  
72      // Number of iterations already handed out.
73      private AtomicLong N1 = new AtomicLong();
74  
75      // Minimum chunk size.
76      private long N2;
77  
78  // Exported constructors.
79      /**
80       * Construct a new self-guided schedule object with a minimum chunk size of
81       * 1.
82       *
83       * @exception IllegalArgumentException (unchecked exception) Thrown if
84       * <code>theChunkSize</code> is less than 1.
85       */
86      public GuidedLongSchedule() {
87          this(1);
88      }
89  
90      /**
91       * Construct a new self-guided schedule object.
92       *
93       * @param theChunkSize Minimum chunk size.
94       * @exception IllegalArgumentException (unchecked exception) Thrown if
95       * <code>theChunkSize</code> is less than 1.
96       */
97      public GuidedLongSchedule(long theChunkSize) {
98          super();
99          if (theChunkSize < 1) {
100             throw new IllegalArgumentException("GuidedLongSchedule(): Minimum chunk size = "
101                     + theChunkSize + " illegal");
102         }
103         N2 = theChunkSize;
104     }
105 
106     /**
107      * Construct a new self-guided schedule object. This constructor is for use
108      * by the <code>LongSchedule.parse()</code> method. <code>args</code> must be an
109      * array of one string, namely the minimum chunk size, an integer &gt;= 1.
110      *
111      * @param args Array of argument strings.
112      * @exception IllegalArgumentException (unchecked exception) Thrown if
113      * <code>args</code> is not an array of one string. Thrown if the minimum chunk
114      * size is less than 1.
115      */
116     public GuidedLongSchedule(String[] args) {
117         this(getChunkSize(args));
118     }
119 
120     private static long getChunkSize(String[] args) {
121         if (args.length != 1) {
122             throw new IllegalArgumentException("GuidedLongSchedule(): Usage: -Dpj.schedule=guided or -Dpj.schedule=\"guided(<n>)\"");
123         }
124         long theChunkSize;
125         try {
126             theChunkSize = Long.parseLong(args[0]);
127         } catch (NumberFormatException exc) {
128             throw new IllegalArgumentException("GuidedLongSchedule(): Chunk size = " + args[0]
129                     + " illegal");
130         }
131         return theChunkSize;
132     }
133 
134 // Exported operations.
135     /**
136      * Determine if this schedule is a fixed schedule. For a parallel team with
137      * <I>K</I> threads, a fixed schedule partitions the loop index range into
138      * exactly <I>K</I> chunks, one chunk for each thread, each chunk with
139      * predetermined upper and lower bounds.
140      *
141      * @return True if this is a fixed schedule, false otherwise.
142      */
143     public boolean isFixedSchedule() {
144         return false;
145     }
146 
147 // Hidden operations.
148     /**
149      * {@inheritDoc}
150      *
151      * Start generating chunks of iterations for a parallel for loop using this
152      * schedule.
153      * <P>
154      * The <code>start()</code> method is only called by a single thread in the
155      * Parallel Java middleware.
156      */
157     public void start(int K,
158             LongRange theLoopRange) {
159         two_K = 2 * K;
160         myLoopRange = theLoopRange;
161         myLoopRangeLength = theLoopRange.length();
162         N1.set(0);
163     }
164 
165     /**
166      * {@inheritDoc}
167      *
168      * Obtain the next chunk of iterations for the given thread index. If there
169      * are more iterations, a range object is returned whose lower bound, upper
170      * bound, and stride specify the chunk of iterations to perform. The
171      * returned range object's stride is the same as that given to the
172      * <code>start()</code> method. The returned range object's lower bound and
173      * upper bound are contained within the range given to the <code>start()</code>
174      * method. If there are no more iterations, null is returned.
175      * <P>
176      * The <code>next()</code> method is called by multiple parallel team threads in
177      * the Parallel Java middleware. The <code>next()</code> method must be multiple
178      * thread safe.
179      */
180     public LongRange next(int theThreadIndex) {
181         for (;;) {
182             long oldN1 = N1.get();
183             LongRange result
184                     = myLoopRange.chunk(oldN1,
185                             Math.max(N2, (myLoopRangeLength - oldN1) / two_K));
186             long N = result.length();
187             if (N == 0) {
188                 return null;
189             }
190             long newN1 = oldN1 + N;
191             if (N1.compareAndSet(oldN1, newN1)) {
192                 return result;
193             }
194         }
195     }
196 
197 }