1 //****************************************************************************** 2 // 3 // File: HybridTeam.java 4 // Package: edu.rit.pj 5 // Unit: Class edu.rit.pj.HybridTeam 6 // 7 // This Java source file is copyright (C) 2010 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.io.IOException; 43 44 import edu.rit.mp.IntegerBuf; 45 import edu.rit.util.Range; 46 47 /** 48 * Class HybridTeam provides a team of threads, distributed across the processes 49 * of a cluster parallel program, for executing a {@linkplain WorkerRegion} in 50 * parallel. 51 * <P> 52 * A hybrid team uses a communicator for message passing. The communicator is 53 * specified as a constructor argument; if not specified, the world communicator 54 * is used. Every process that is part of the communicator must create the 55 * hybrid team. In class HybridTeam, there are one or more worker threads per 56 * process; typically, the number of threads in each process equals the number 57 * of CPUs on the node executing the process. (To get just one worker thread per 58 * process, use class {@linkplain WorkerTeam}.) Every worker thread in every 59 * process has a unique index, going from index 0 for the first thread in the 60 * first process to index <I>K</I>−1 for the last thread in the last 61 * process, where <I>K</I> is the total number of worker threads in all the 62 * processes. In process rank 0, there is an additional master thread. 63 * <P> 64 * When a hybrid team is constructed, the processes in the communicator send 65 * messages amongst themselves to make every process aware of the number of 66 * worker threads in each process. These messages use a tag of 67 * <code>Integer.MIN_VALUE</code>. If an I/O error occurs during this message 68 * passing, the constructor throws an IOException. 69 * <P> 70 * To execute a worker region, create a HybridTeam object; create an instance of 71 * a concrete subclass of class {@linkplain WorkerRegion}; and pass this 72 * instance to the hybrid team's <code>execute()</code> method. For further 73 * information, see class {@linkplain WorkerRegion}. 74 * 75 * @author Alan Kaminsky 76 * @version 18-Nov-2009 77 */ 78 public class HybridTeam 79 extends WorkerTeam { 80 81 // Hidden data members. 82 // Array of lowest worker indexes, indexed by process rank. 83 int[] lowestIndex; 84 85 // Exported constructors. 86 /** 87 * Construct a new hybrid team with the default number of threads per 88 * process and using the world communicator for message passing. If the 89 * <code>"pj.nt"</code> Java property is specified, that property gives the 90 * default number of threads per process, which must be an integer greater 91 * than or equal to 1. If the <code>"pj.nt"</code> Java property is not 92 * specified, the default number of threads in each process is the value 93 * returned by the <code>Runtime.availableProcessors()</code> method. You can 94 * specify the default number of threads per process on the Java command 95 * line like this: 96 * <PRE> 97 * java -Dpj.nt=4 . . . 98 * </PRE> 99 * 100 * @exception IllegalArgumentException (unchecked exception) Thrown if the 101 * <code>"pj.nt"</code> property value is not an integer greater than or equal 102 * to 1. 103 * @exception IOException Thrown if an I/O error occurred. 104 * @throws java.io.IOException if any. 105 */ 106 public HybridTeam() 107 throws IOException { 108 this(getDefaultThreadCount(), Comm.world()); 109 } 110 111 /** 112 * Construct a new hybrid team with the given number of threads per process 113 * and using the world communicator for message passing. 114 * 115 * @param K Number of threads per process. 116 * @exception IllegalArgumentException (unchecked exception) Thrown if 117 * <I>K</I> is less than 1. 118 * @exception IOException Thrown if an I/O error occurred. 119 * @throws java.io.IOException if any. 120 */ 121 public HybridTeam(int K) 122 throws IOException { 123 this(K, Comm.world()); 124 } 125 126 /** 127 * Construct a new hybrid team with the default number of threads per 128 * process and using the given communicator for message passing. If the 129 * <code>"pj.nt"</code> Java property is specified, that property gives the 130 * default number of threads per process, which must be an integer greater 131 * than or equal to 1. If the <code>"pj.nt"</code> Java property is not 132 * specified, the default number of threads in each process is the value 133 * returned by the <code>Runtime.availableProcessors()</code> method. You can 134 * specify the default number of threads per process on the Java command 135 * line like this: 136 * <PRE> 137 * java -Dpj.nt=4 . . . 138 * </PRE> 139 * 140 * @param comm Communicator to use for message passing. 141 * @exception IllegalArgumentException (unchecked exception) Thrown if the 142 * <code>"pj.nt"</code> property value is not an integer greater than or equal 143 * to 1. 144 * @exception NullPointerException (unchecked exception) Thrown if 145 * <code>comm</code> is null. 146 * @exception IOException Thrown if an I/O error occurred. 147 * @throws java.io.IOException if any. 148 */ 149 public HybridTeam(Comm comm) 150 throws IOException { 151 this(getDefaultThreadCount(), comm); 152 } 153 154 /** 155 * Construct a new hybrid team with the given number of threads per process 156 * and using the given communicator for message passing. 157 * 158 * @param K Number of threads per process. 159 * @param comm Communicator to use for message passing. 160 * @exception IllegalArgumentException (unchecked exception) Thrown if 161 * <I>K</I> is less than 1. 162 * @exception NullPointerException (unchecked exception) Thrown if 163 * <code>comm</code> is null. 164 * @exception IOException Thrown if an I/O error occurred. 165 * @throws java.io.IOException if any. 166 */ 167 public HybridTeam(int K, 168 Comm comm) 169 throws IOException { 170 // Construct superclass but don't initialize it yet. 171 super(false); 172 173 // Verify preconditions. 174 if (K <= 0) { 175 throw new IllegalArgumentException("HybridTeam(): K = " + K + " illegal"); 176 } 177 if (comm == null) { 178 throw new NullPointerException("HybridTeam(): comm is null"); 179 } 180 181 // Communicate with the other processes to determine the number of 182 // worker threads in each process; store these in lowestIndex. 183 int size = comm.size(); 184 int rank = comm.rank(); 185 lowestIndex = new int[size + 1]; 186 lowestIndex[rank] = K; 187 IntegerBuf[] bufs 188 = IntegerBuf.sliceBuffers(lowestIndex, 189 new Range(0, size - 1).subranges(size)); 190 IntegerBuf myBuf = bufs[rank]; 191 comm.allGather(Integer.MIN_VALUE, myBuf, bufs); 192 193 // Scan lowestIndex to determine the lowest worker index in each process 194 // and the total number of worker threads. 195 int count = 0; 196 for (int i = 0; i < size; ++i) { 197 int tmp = lowestIndex[i]; 198 lowestIndex[i] = count; 199 count += tmp; 200 } 201 lowestIndex[size] = count; 202 203 // Initialize superclass. 204 initialize(/*K */K, 205 /*comm */ comm, 206 /*size */ size, 207 /*rank */ rank, 208 /*count*/ count, 209 /*wlb */ lowestIndex[rank]); 210 } 211 212 // Exported operations. 213 /** 214 * Determine the default number of threads per process for a hybrid team. If 215 * the <code>"pj.nt"</code> Java property is specified, that property gives the 216 * default number of threads per process, which must be an integer greater 217 * than or equal to 1. If the <code>"pj.nt"</code> Java property is not 218 * specified, the default number of threads in each process is the value 219 * returned by the <code>Runtime.availableProcessors()</code> method. You can 220 * specify the default number of threads per process on the Java command 221 * line like this: 222 * <PRE> 223 * java -Dpj.nt=4 . . . 224 * </PRE> 225 * 226 * @return Default number of threads per process for a hybrid team. 227 * @exception IllegalArgumentException (unchecked exception) Thrown if the 228 * <code>"pj.nt"</code> property value is not an integer greater than or equal 229 * to 1. 230 */ 231 public static int getDefaultThreadCount() { 232 int k = PJProperties.getPjNt(); 233 if (k == 0) { 234 k = Runtime.getRuntime().availableProcessors(); 235 } 236 return k; 237 } 238 239 /** 240 * {@inheritDoc} 241 * 242 * Determine the rank of the process that contains the worker thread with 243 * the given index. 244 * @exception IllegalArgumentException (unchecked exception) Thrown if 245 * <code>w</code> is not in the range 0 .. 246 * <code>getTotalThreadCount()</code>−1. 247 */ 248 public int workerRank(int w) { 249 // Verify preconditions. 250 if (0 > w || w >= count) { 251 throw new IllegalArgumentException("HybridTeam.workerRank(): w (= " + w + ") illegal"); 252 } 253 254 // Do a binary search to find w in lowestIndex. 255 int L = 0; 256 int U = size; 257 while (U - L > 1) { 258 // Invariant: w >= lowestIndex[L] and w < lowestIndex[U]. 259 int M = (L + U) / 2; 260 if (w >= lowestIndex[M]) { 261 L = M; 262 } else { 263 U = M; 264 } 265 } 266 267 // Assert: L = U-1 and w >= lowestIndex[L] and w < lowestIndex[U]. 268 return L; 269 } 270 271 }