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 }