1 //******************************************************************************
2 //
3 // File: IntegerSchedule.java
4 // Package: edu.rit.pj
5 // Unit: Class edu.rit.pj.IntegerSchedule
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.lang.reflect.Constructor;
43 import java.lang.reflect.InvocationTargetException;
44
45 import edu.rit.util.Range;
46
47 /**
48 * Class IntegerSchedule provides an object that determines how to schedule the
49 * iterations of a {@linkplain ParallelForLoop} among the threads in a
50 * {@linkplain ParallelTeam}. The loop index is type <code>int</code>.
51 * <p>
52 * To create a schedule object, call one of the following static methods:
53 * <UL>
54 * <LI><code>IntegerSchedule.fixed()</code>
55 * <LI><code>IntegerSchedule.dynamic()</code>
56 * <LI><code>IntegerSchedule.guided()</code>
57 * <LI><code>IntegerSchedule.runtime()</code>
58 * <LI><code>IntegerSchedule.parse()</code>
59 * </UL>
60 * <p>
61 * The Parallel Java Library includes three built-in schedule implementations:
62 * fixed, dynamic, and guided. You can create instances of these by calling the
63 * <code>fixed()</code>, <code>dynamic()</code>, and <code>guided()</code> methods. You can
64 * also create your own schedule implementation by writing a subclass of class
65 * IntegerSchedule. The subclass must have a no-argument constructor and a
66 * constructor whose argument is an array of Strings; see the <code>parse()</code>
67 * method for further information about how these constructors are used.
68 *
69 * @author Alan Kaminsky
70 * @version 17-Nov-2009
71 */
72 public abstract class IntegerSchedule
73 extends Schedule {
74
75 // Hidden data members.
76 // Loop index for ordered() construct.
77 volatile int myOrderedIndex;
78
79 // Hidden constructors.
80
81 /**
82 * Construct a new schedule object.
83 */
84 protected IntegerSchedule() {
85 super();
86 }
87
88 // Exported operations.
89
90 /**
91 * Returns a fixed schedule object. The loop iterations are apportioned
92 * among the parallel team threads once at the beginning of the parallel for
93 * loop, with each thread getting a fixed number of iterations, the same
94 * number of iterations for each thread (plus or minus one).
95 *
96 * @return Fixed schedule object.
97 */
98 public static IntegerSchedule fixed() {
99 return new FixedIntegerSchedule();
100 }
101
102 /**
103 * Returns a dynamic schedule object with a chunk size of 1. The loop
104 * iterations are apportioned into chunks of size 1 (one iteration per
105 * chunk). Each parallel team thread repeatedly performs the next available
106 * iteration until there are no more iterations.
107 *
108 * @return Dynamic schedule object.
109 */
110 public static IntegerSchedule dynamic() {
111 return new DynamicIntegerSchedule(1);
112 }
113
114 /**
115 * Returns a dynamic schedule object with the given chunk size. The loop
116 * iterations are apportioned into chunks of size <code>theChunkSize</code>
117 * (<code>theChunkSize</code> iterations per chunk). Each parallel team thread
118 * repeatedly performs the next available chunk of iterations until there
119 * are no more chunks. The final chunk may be smaller than
120 * <code>theChunkSize</code>.
121 *
122 * @param theChunkSize Chunk size, >= 1.
123 * @return Dynamic schedule object.
124 * @throws java.lang.IllegalArgumentException (unchecked exception) Thrown if
125 * <code>theChunkSize</code> < 1.
126 */
127 public static IntegerSchedule dynamic(int theChunkSize) {
128 return new DynamicIntegerSchedule(theChunkSize);
129 }
130
131 /**
132 * Returns a self-guided schedule object with a minimum chunk size of 1. The
133 * loop iterations are apportioned into chunks of exponentially diminishing
134 * sizes. Each successive chunk's size is half the remaining number of
135 * iterations divided by the number of threads in the parallel team.
136 * However, each chunk's size is at least 1 (a minimum of one iteration per
137 * chunk). Each parallel team thread repeatedly performs the next available
138 * chunk of iterations until there are no more chunks.
139 *
140 * @return Self-guided schedule object.
141 */
142 public static IntegerSchedule guided() {
143 return new GuidedIntegerSchedule(1);
144 }
145
146 /**
147 * Returns a self-guided schedule object with the given minimum chunk size.
148 * The loop iterations are apportioned into chunks of exponentially
149 * diminishing sizes. Each successive chunk's size is half the remaining
150 * number of iterations divided by the number of threads in the parallel
151 * team. However, each chunk is at least <code>theChunkSize</code> (a minimum of
152 * <code>theChunkSize</code> iterations per chunk). Each parallel team thread
153 * repeatedly performs the next available chunk of iterations until there
154 * are no more chunks. The final chunk may be smaller than
155 * <code>theChunkSize</code>.
156 *
157 * @param theChunkSize Minimum chunk size, >= 1.
158 * @return Self-guided schedule object.
159 * @throws java.lang.IllegalArgumentException (unchecked exception) Thrown if
160 * <code>theChunkSize</code> < 1.
161 */
162 public static IntegerSchedule guided(int theChunkSize) {
163 return new GuidedIntegerSchedule(theChunkSize);
164 }
165
166 /**
167 * Returns a schedule object of a type determined at run time. If the
168 * <code>"pj.schedule"</code> Java property is specified, the property's value
169 * is parsed by the <code>parse()</code> method, and that gives the type of
170 * schedule. If the <code>"pj.schedule"</code> Java property is not specified,
171 * the default is a fixed schedule. You can specify the schedule on the Java
172 * command line like this (note that quotation marks may be needed):
173 * <PRE>
174 * java -Dpj.schedule="dynamic(5)" . . .
175 * </PRE>
176 *
177 * @return Schedule object.
178 * @throws java.lang.IllegalArgumentException (unchecked exception) Thrown if the
179 * <code>"pj.schedule"</code> property value cannot be parsed.
180 */
181 public static IntegerSchedule runtime() {
182 return runtime(IntegerSchedule.fixed());
183 }
184
185 /**
186 * Returns a schedule object of a type determined at run time, using the
187 * given default schedule. If the <code>"pj.schedule"</code> Java property is
188 * specified, the property's value is parsed by the <code>parse()</code> method,
189 * and that gives the type of schedule. If the <code>"pj.schedule"</code> Java
190 * property is not specified, the given <code>defaultSchedule</code> is
191 * returned. You can specify the schedule on the Java command line like this
192 * (note that quotation marks may be needed):
193 * <PRE>
194 * java -Dpj.schedule="dynamic(5)" . . .
195 * </PRE>
196 *
197 * @param defaultSchedule Schedule to use if the <code>"pj.schedule"</code>
198 * Java property is not specified.
199 * @return Schedule object.
200 * @throws java.lang.IllegalArgumentException (unchecked exception) Thrown if the
201 * <code>"pj.schedule"</code> property value cannot be parsed.
202 */
203 public static IntegerSchedule runtime(IntegerSchedule defaultSchedule) {
204 String s = PJProperties.getPjSchedule();
205 return s == null ? defaultSchedule : parse(s);
206 }
207
208 /**
209 * Returns a schedule object of a type determined by parsing the given
210 * string. The string must be one of the following:
211 * <UL>
212 * <LI><code>"fixed"</code> -- Fixed schedule.
213 * <LI><code>"dynamic"</code> -- Dynamic schedule with a chunk size of 1.
214 * <LI><code>"dynamic(<I>n</I>)"</code> -- Dynamic schedule with a chunk size of
215 * <code><I>n</I></code>, an integer >= 1.
216 * <LI><code>"guided"</code> -- Self-guided schedule with a minimum chunk size
217 * of 1.
218 * <LI><code>"guided(<I>n</I>)"</code> -- Self-guided schedule with a minimum
219 * chunk size of <code><I>n</I></code>, an integer >= 1.
220 * <LI><code>"<I>classname</I>"</code> -- Schedule that is an instance of the
221 * given class. <I>classname</I> is the fully-qualified class name of the
222 * schedule class, which must be a subclass of class IntegerSchedule. The
223 * instance is constructed using the subclass's no-argument constructor.
224 * <LI><code>"<I>classname</I>(<I>arg</I>,<I>arg</I>,...)"</code> -- Schedule
225 * that is an instance of the given class. <I>classname</I> is the
226 * fully-qualified class name of the schedule class, which must be a
227 * subclass of class IntegerSchedule. The arguments between the parentheses
228 * are split into separate strings separated by commas. There cannot be
229 * parentheses or commas within the arguments themselves. The instance is
230 * constructed using the subclass's constructor whose argument is an array
231 * of Strings, namely the individual arguments between the parentheses.
232 * </UL>
233 *
234 * @param s String to parse.
235 * @return Schedule object.
236 * @throws java.lang.NullPointerException (unchecked exception) Thrown if
237 * <code>s</code> is null.
238 * @throws java.lang.IllegalArgumentException (unchecked exception) Thrown if
239 * <code>s</code> is not one of the above.
240 */
241 public static IntegerSchedule parse(String s) {
242 try {
243 int p1 = s.indexOf('(');
244
245 if (p1 == -1) {
246 // No arguments. s is the subclass name. Get subclass.
247 Class<?> subclass
248 = Class.forName(getSubclassName(s),
249 true,
250 Thread.currentThread().getContextClassLoader());
251
252 // Instantiate subclass using no-argument constructor.
253 return (IntegerSchedule) subclass.getConstructor().newInstance();
254 } else {
255 // Arguments. Verify syntax.
256 int p2 = s.indexOf(')');
257 if (p2 != s.length() - 1) {
258 throw new IllegalArgumentException("IntegerSchedule.parse(): Schedule \"" + s
259 + "\" illegal");
260 }
261
262 // Split arguments around commas.
263 String[] args = s.substring(p1 + 1, p2).split(",");
264
265 // s up to '(' is the subclass name. Get subclass.
266 Class<?> subclass
267 = Class.forName(getSubclassName(s.substring(0, p1)),
268 true,
269 Thread.currentThread().getContextClassLoader());
270
271 // Get constructor with one String[] argument.
272 Constructor<?> constructor
273 = subclass.getConstructor(String[].class);
274
275 // Instantiate subclass using String[]-argument constructor.
276 return (IntegerSchedule) constructor.newInstance((Object) args);
277 }
278 } catch (ClassCastException exc) {
279 throw new IllegalArgumentException("IntegerSchedule.parse(): Schedule \"" + s + "\" illegal",
280 exc);
281 } catch (ClassNotFoundException exc) {
282 throw new IllegalArgumentException("IntegerSchedule.parse(): Schedule \"" + s + "\" illegal",
283 exc);
284 } catch (IllegalAccessException exc) {
285 throw new IllegalArgumentException("IntegerSchedule.parse(): Schedule \"" + s + "\" illegal",
286 exc);
287 } catch (InstantiationException exc) {
288 throw new IllegalArgumentException("IntegerSchedule.parse(): Schedule \"" + s + "\" illegal",
289 exc);
290 } catch (InvocationTargetException exc) {
291 throw new IllegalArgumentException("IntegerSchedule.parse(): Schedule \"" + s + "\" illegal",
292 exc);
293 } catch (NoSuchMethodException exc) {
294 throw new IllegalArgumentException("IntegerSchedule.parse(): Schedule \"" + s + "\" illegal",
295 exc);
296 }
297 }
298
299 /**
300 * Determine if this schedule is a fixed schedule. For a parallel team with
301 * <I>K</I> threads, a fixed schedule partitions the loop index range into
302 * exactly <I>K</I> chunks, one chunk for each thread, each chunk with
303 * predetermined upper and lower bounds.
304 *
305 * @return True if this is a fixed schedule, false otherwise.
306 */
307 public abstract boolean isFixedSchedule();
308
309 // Hidden operations.
310
311 /**
312 * Get the name of the subclass to instantiate. The names <code>"fixed"</code>,
313 * <code>"dynamic"</code>, and <code>"guided"</code> are recognized as special
314 * cases.
315 *
316 * @param name Subclass name, or special case string.
317 * @return Subclass name.
318 */
319 private static String getSubclassName(String name) {
320 if (name.equals("fixed")) {
321 return "edu.rit.pj.FixedIntegerSchedule";
322 } else if (name.equals("dynamic")) {
323 return "edu.rit.pj.DynamicIntegerSchedule";
324 } else if (name.equals("guided")) {
325 return "edu.rit.pj.GuidedIntegerSchedule";
326 } else {
327 return name;
328 }
329 }
330
331 /**
332 * Start a parallel for loop using this schedule. This method performs
333 * common processing, then calls the subclass-specific <code>start()</code>
334 * method.
335 *
336 * @param K Number of threads in the parallel team.
337 * @param theLoopRange Range of iterations for the entire parallel for loop.
338 * The stride may be 1 or greater.
339 */
340 void commonStart(int K,
341 Range theLoopRange) {
342 myBreak = false;
343 myOrderedIndex = theLoopRange.lb();
344 start(K, theLoopRange);
345 }
346
347 /**
348 * Start generating chunks of iterations for a parallel for loop using this
349 * schedule.
350 * <p>
351 * The <code>start()</code> method is only called by a single thread in the
352 * Parallel Java middleware.
353 *
354 * @param K Number of threads in the parallel team.
355 * @param theLoopRange Range of iterations for the entire parallel for loop.
356 * The stride may be 1 or greater.
357 */
358 public abstract void start(int K,
359 Range theLoopRange);
360
361 /**
362 * Obtain the next chunk of iterations for the given thread index. This
363 * method performs common processing, then calls the subclass-specific
364 * <code>next()</code> method.
365 *
366 * @param theThreadIndex Thread index in the range 0 .. <I>K</I>-1.
367 * @return Chunk of iterations, or null if no more iterations.
368 */
369 Range commonNext(int theThreadIndex) {
370 if (myBreak) {
371 return null;
372 } else {
373 return next(theThreadIndex);
374 }
375 }
376
377 /**
378 * Obtain the next chunk of iterations for the given thread index. If there
379 * are more iterations, a range object is returned whose lower bound, upper
380 * bound, and stride specify the chunk of iterations to perform. The
381 * returned range object's stride is the same as that given to the
382 * <code>start()</code> method. The returned range object's lower bound and
383 * upper bound are contained within the range given to the <code>start()</code>
384 * method. If there are no more iterations, null is returned.
385 * <p>
386 * The <code>next()</code> method is called by multiple parallel team threads in
387 * the Parallel Java middleware. The <code>next()</code> method must be multiple
388 * thread safe.
389 *
390 * @param theThreadIndex Thread index in the range 0 .. <I>K</I>-1.
391 * @return Chunk of iterations, or null if no more iterations.
392 */
393 public abstract Range next(int theThreadIndex);
394
395 // Unit test main program.
396 // /**
397 // * Unit test main program.
398 // */
399 // public static void main
400 // (String[] args)
401 // {
402 // if (args.length != 4)
403 // {
404 // System.err.println ("Usage: java [-Dpj.schedule=<SCHEDULE>] edu.rit.pj.IntegerSchedule <K> <lb> <ub> <stride>");
405 // System.exit (1);
406 // }
407 // int K = Integer.parseInt (args[0]);
408 // int lb = Integer.parseInt (args[1]);
409 // int ub = Integer.parseInt (args[2]);
410 // int stride = Integer.parseInt (args[3]);
411 // IntegerSchedule schedule = IntegerSchedule.runtime();
412 // schedule.start (K, new Range (lb, ub, stride));
413 // Range chunk;
414 // for (int k = 0; k < K; ++ k)
415 // {
416 // while ((chunk = schedule.next (k)) != null)
417 // {
418 // System.out.println ("Thread " + k + " chunk = " + chunk);
419 // }
420 // }
421 // }
422 }