View Javadoc
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>&minus;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>&minus;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 }