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 }