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