//******************************************************************************
//
// File:    Comm.java
// Package: edu.rit.pj
// Unit:    Class edu.rit.pj.Comm
//
// This Java source file is copyright (C) 2009 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************

package edu.rit.pj;

import edu.rit.mp.Buf;
import edu.rit.mp.Channel;
import edu.rit.mp.ChannelGroup;
import edu.rit.mp.ConnectListener;
import edu.rit.mp.IORequest;
import edu.rit.mp.IntegerBuf;
import edu.rit.mp.ObjectBuf;
import edu.rit.mp.Status;

import edu.rit.pj.cluster.CommPattern;
import edu.rit.pj.cluster.JobBackend;
import edu.rit.pj.cluster.JobFrontend;
import edu.rit.pj.cluster.JobSchedulerException;

import edu.rit.pj.reduction.IntegerOp;
import edu.rit.pj.reduction.Op;

import edu.rit.util.Range;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.PrintStream;

import java.net.InetAddress;
import java.net.InetSocketAddress;

import java.util.HashMap;
import java.util.LinkedList;

/**
 * Class Comm provides a communicator for a PJ cluster parallel program. Class
 * Comm provides a method to initialize the PJ message passing middleware and
 * run the parallel program on multiple processors of a cluster parallel
 * computer. Class Comm also provides methods for passing messages between the
 * processes of the parallel program.
 * 

*


*

* BASIC CONCEPTS *

* A cluster parallel computer typically consists of a frontend * processor and a number of backend processors connected via a * dedicated high-speed network. A user logs into the frontend processor and * runs a PJ program there. The PJ message passing middleware causes copies of * the PJ program to run in a number of separate processes, each process on a * different backend processor. The backend processes run the PJ program, using * the PJ middleware to send messages amongst themselves. The PJ middleware * redirects the backend processes' standard output and standard error streams * to the frontend process. The frontend process does not actually execute the * PJ program, but merely displays all the backend processes' standard output * and standard error streams on the frontend process's own standard output and * standard error. *

* For the PJ message passing middleware to work, certain server processes must * be running. See package {@linkplain edu.rit.pj.cluster} for further * information. *

* To initialize the PJ message passing middleware, the program must first call * the static Comm.init() method, passing in the command line * arguments. *

* A communicator is associated with a group of backend processes. The * communicator's size is the number of processes in the group. Each * process in the communicator has a different rank in the range 0 .. * size-1. A process may obtain the size and rank by calling the * communicator's size() and rank() methods. *

* There is one predefined communicator, the world communicator, * consisting of all the backend processes in the parallel program. A process * may obtain a reference to the world communicator by calling the static * Comm.world() method. Typically, the first few lines in a PJ cluster * parallel program look like this: *

 *     public class AParallelProgram
 *         {
 *         public static void main
 *             (String[] args)
 *             throws Exception
 *             {
 *             Comm.init (args);
 *             Comm world = Comm.world();
 *             int size = world.size();
 *             int rank = world.rank();
 *             . . .
*

* The number of processes in the parallel program -- that is, the size of the * world communicator -- is specified by the "pj.np" property, which * must be an integer greater than or equal to 1. You can specify the number of * processes on the Java command line like this: *

 *     java -Dpj.np=4 . . .
*

* If the "pj.np" property is not specified, the default is 1. *

* The PJ program will run on the specified number of backend processors as * described above. To determine which backend processors to use, the PJ program * interacts with a Job Scheduler server process running on the frontend * processor. When the PJ program starts and calls the Comm.init() * method, the middleware first prints the job number on the standard error. The * middleware then waits until the required number of backend processors are * ready to run a job. As each backend processor becomes ready, the middleware * prints on the standard error the name of each backend processor assigned to * the job. Once all are ready, the PJ program starts running on those backend * processors, and all further output comes from the PJ program. Since each PJ * program interacts with the Job Scheduler, the Job Scheduler can ensure that * each backend processor is running a backend process for only one job at a * time. *

* Depending on the system load, your PJ program may have to wait in the Job * Scheduler's queue for a while until enough backend processors become ready. * If you get tired of waiting, you can kill your PJ program (e.g., by typing * CTRL-C), which will remove your PJ program from the Job Scheduler's queue. *

* The Job Scheduler has a web interface that lets you examine the cluster * status. Just point your web browser at this URL: *

*     http://<hostname>:8080/ *

* where <hostname> is replaced by the host name of the frontend * processor. The default port for the cluster status web interface is port * 8080. The Job Scheduler can be configured to use a different port. For * further information, see package {@linkplain edu.rit.pj.cluster}. *

* If the PJ program is executed on a host where no Job Scheduler is running, * the PJ program will run in one process on that host (i.e., the machine * you're logged into), rather than on the backend processors. The message * passing methods in class Comm will still work, though. This option can be * useful for debugging a PJ program's logic on a non-parallel machine before * running the PJ program on a cluster. *

*


*

* MESSAGE PASSING *

* PJ provides two categories of communication, point-to-point * communication and collective communication. The following methods * of class Comm are used for point-to-point communication: *

* The following methods are used for collective communication: * * These methods are described further in the sections below. *

* In addition, you can create a new communicator consisting of all, or a subset * of, the processes in an existing communicator. Message passing in the new * communicator is completely independent of message passing in the original * communicator. The following method creates a new communicator: *

*

*


*

* POINT-TO-POINT COMMUNICATION *

* One process in a PJ cluster parallel program, the source process, may * use a communicator to send a message to another process in the program, the * destination process. This is called a point-to-point * communication because just the two processes are involved (as opposed to * a collective communication, which involves all the processes). Five * point-to-point communication methods are available in this release: send, * receive, send-receive, flood-send, and flood-receive. *

* Send and Receive *

* To do a point-to-point communication, the source process calls the * send() method on a certain communicator, such as the world * communicator. The source process specifies the destination process's rank, * the tag for the message, and a buffer containing the data items * to be sent (type {@linkplain edu.rit.mp.Buf}). Likewise, the destination * process calls the receive() method on the same communicator as the * source process. The destination process specifies the source process's rank, * the message tag which must be the same as in the source process, and the * buffer for the data items to be received. *

* A send() method call and a receive() method call are said * to match if (a) the rank passed to the send() method equals * the rank of the process calling receive(), (b) the rank passed to * the receive() method equals the rank of the process calling * send(), (c) the item data type in the source buffer is the same as * the item data type in the destination buffer, and (d) the send message tag * equals the receive message tag. A receive() method call will block * until a matching send() method call occurs. If more than one * send() method call matches a receive() method call, one of * the matching send() method calls is picked in an unspecified manner. * A send() method call may block until a matching * receive() method call occurs due to flow control in the underlying * network communication. *

* The message tag can be used to distinguish different kinds of messages. A * receive() method call will only match a send() method call * with the same tag. If there is no need to distinguish different kinds of * messages, omit the tag (it will default to 0). *

* Once a send() method call and a receive() method call have * been matched together, the actual message data transfer takes place. Each * item in the source buffer, starting at index 0 and continuing for the entire * length of the source buffer, is written to the message. At the other end, * each item in the destination buffer, starting at index 0, is read from the * message. *

* The receive() method returns a {@linkplain CommStatus} object. The * status object gives the actual rank of the process that sent the message, the * actual message tag that was received, and the actual number of data items in * the message. If the actual number of data items in the message is less than * the length of the destination buffer, nothing is stored into the extra data * items at the end of the destination buffer. If the actual number of data * items in the message is greater than the length of the destination buffer, * the extra data items at the end of the message are discarded. *

* The send() method does not return until all the message elements * have been written from the source buffer. Likewise, the receive() * method does not return until all the message elements have been read into the * destination buffer. However, you cannot assume that because the * send() method has returned, the matching receive() method * has also returned. Because of buffering in the underlying network * communication, not all the destination items might have been received even * though all the source items have been sent. *

* The destination process, instead of specifying a particular source process, * can declare that it will receive a message from any source process by * specifying null for the source process rank in the receive() method * call. This is called a wildcard source. In this case the * receive() method call's returned status object will indicate the * actual source process that sent the message. *

* The destination process, instead of specifying a particular message tag, can * declare that it will receive a message with any tag by specifying null for * the tag in the receive() method call. This is called a wildcard * tag. Alternatively, the destination process can specify a range of * message tags, and it will receive a message with any tag in the given range. * In these cases the receive() method call's returned status object * will indicate the actual message tag that was sent. *

* A process can send a message to itself. In this case one thread must call * send() (specifying the process's own rank as the destination) and a * different thread must call receive() (specifying the process's own * rank as the source), otherwise a deadlock will ensue. *

* Send-Receive *

* By calling the sendReceive() method, a process can send a buffer of * outgoing message items to a destination process while simultaneously * receiving a buffer of incoming message items from a source process. The * destination process may be the same as the source process, or different from * the source process. The outgoing message items must come from a different * place than where the incoming message items will be stored, otherwise the * incoming message items may overwrite the outgoing message items before they * can be sent. When the sendReceive() method returns, the outgoing * message items have been fully sent, but they may not yet have been fully * received; and the incoming message items have been fully received. *

* With the sendReceive() method, a process cannot receive a message * from a wildcard source, and a process cannot receive a message with a * wildcard tag or a range of tags. The process calling sendReceive() * must know the rank of the source process and the message tag (if not 0). The * sendReceive() method does return a status object giving the outcome * of the receive half of the send-receive operation, just as the * receive() method does. *

* A process can send-receive messages with itself. In this case one thread must * call sendReceive() (specifying the process's own rank as the source * and destination) and a different thread must also call sendReceive() * (specifying the process's own rank as the source and destination), otherwise * a deadlock will ensue. *

* Non-Blocking Communication *

* The send(), receive(), and sendReceive() methods * each have a non-blocking version. A non-blocking communication method * initiates the communication operation and immediately returns, storing the * state of the communication operation in a {@linkplain CommRequest} object. * The communicator then performs the communication operation in a separate * thread. This allows the calling thread to do other work while the * communication operation is in progress. To wait for the send and receive * operations to finish, call the CommRequest object's waitForFinish() * method. *

* Flood-Send and Flood-Receive *

* Any process can send a message to all processes in the communicator. This is * called "flooding" the message. First, all processes must start a * flood-receive operation, either by calling the non-blocking * floodReceive() method, or by having a separate thread call the * blocking floodReceive() method. Then, one process (any process) must * call the floodSend() method. The data items in the flood-send * operation's outgoing buffer are copied into the flood-receive operation's * incoming buffer in all processes. *

* Message flooding is similar to the "broadcast" collective communication * operation (see below). The differences are these: Broadcasting combines * sending and receiving in a single operation; flooding uses separate send and * receive operations. For broadcasting, every process must know which process * is sending the outgoing data items; for flooding, the receiving processes do * not need to know which process is sending (any process can send). *

*


*

* COLLECTIVE COMMUNICATION *

* A PJ cluster parallel program may use a communicator to send a message among * all the processes in the program at the same time. This is called a * collective communication because all the processes in the communicator * are involved (as opposed to a point-to-point communication). Ten collective * communication methods are available in this release: broadcast, scatter, * gather, all-gather, reduce, all-reduce, all-to-all, scan, exclusive-scan, and * barrier. Further collective communication methods will be added to class Comm * in a later release. *

* Broadcast *

* One process in the communicator, the root process, has a source buffer * (type {@linkplain edu.rit.mp.Buf Buf}) filled with data. The other processes * in the communicator each have a destination buffer with the same length and * the same item data type as the source buffer. Each process calls the * communicator's broadcast() method. Afterwards, all the destination * buffers contain the same data as the source buffer. *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |    |    |    |    |    |
 * |  2 |    |    |    |    |    |    |
 * |  3 |    |    |    |    |    |    |
 * |  4 |    |    |    |    |    |    |
 * |  5 |    |    |    |    |    |    |
 * |  6 |    |    |    |    |    |    |
 * |  7 |    |    |    |    |    |    |
 * |  8 |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
*
*
* After: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  1 |    |  1 |    |  1 |
 * |  2 |    |  2 |    |  2 |    |  2 |
 * |  3 |    |  3 |    |  3 |    |  3 |
 * |  4 |    |  4 |    |  4 |    |  4 |
 * |  5 |    |  5 |    |  5 |    |  5 |
 * |  6 |    |  6 |    |  6 |    |  6 |
 * |  7 |    |  7 |    |  7 |    |  7 |
 * |  8 |    |  8 |    |  8 |    |  8 |
 * +----+    +----+    +----+    +----+
*
*
* Note: Any process can be the root of the broadcast. The above is only * one example with process 0 as the root. *

* Scatter *

* One process in the communicator, the root process, has K source * buffers (type {@linkplain edu.rit.mp.Buf Buf}) filled with data, where * K is the size of the communicator. For example, the source buffers * could be different portions of an array. Each process in the communicator * (including the root process) has a destination buffer with the same length * and the same item data type as the corresponding source buffer. Each process * calls the communicator's scatter() method. Afterwards, each * process's destination buffer contains the same data as the corresponding * source buffer in the root process. *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+
 * |  1 |
 * |  2 |
 * +----+
 * |  3 |
 * |  4 |
 * +----+
 * |  5 |
 * |  6 |
 * +----+
 * |  7 |
 * |  8 |
 * +----+
 *
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |    |
 * |    |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
*
*
* After: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+
 * |  1 |
 * |  2 |
 * +----+
 * |  3 |
 * |  4 |
 * +----+
 * |  5 |
 * |  6 |
 * +----+
 * |  7 |
 * |  8 |
 * +----+
 *
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
*
*
* In the root process, the destination buffer can be the same as the source * buffer: *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+
 * |  1 |
 * |  2 |
 * +----+    +----+
 * |  3 |    |    |
 * |  4 |    |    |
 * +----+    +----+    +----+
 * |  5 |              |    |
 * |  6 |              |    |
 * +----+              +----+    +----+
 * |  7 |                        |    |
 * |  8 |                        |    |
 * +----+                        +----+
*
*
* After: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+
 * |  1 |
 * |  2 |
 * +----+    +----+
 * |  3 |    |  3 |
 * |  4 |    |  4 |
 * +----+    +----+    +----+
 * |  5 |              |  5 |
 * |  6 |              |  6 |
 * +----+              +----+    +----+
 * |  7 |                        |  7 |
 * |  8 |                        |  8 |
 * +----+                        +----+
*
*
* Note: Any process can be the root of the scatter. The above is only * one example with process 0 as the root. *

* Gather *

* Gather is the opposite of scatter. One process in the communicator, the root * process, has K destination buffers (type {@linkplain edu.rit.mp.Buf * Buf}), where K is the size of the communicator. For example, the * destination buffers could be different portions of an array. Each process in * the communicator (including the root process) has a source buffer with the * same length and the same item data type as the corresponding destination * buffer, filled with data. Each process calls the communicator's * gather() method. Afterwards, each destination buffer in the root * process contains the same data as the corresponding source buffer. *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
 *
 * +----+
 * |    |
 * |    |
 * +----+
 * |    |
 * |    |
 * +----+
 * |    |
 * |    |
 * +----+
 * |    |
 * |    |
 * +----+
*
*
* After: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
 *
 * +----+
 * |  1 |
 * |  2 |
 * +----+
 * |  3 |
 * |  4 |
 * +----+
 * |  5 |
 * |  6 |
 * +----+
 * |  7 |
 * |  8 |
 * +----+
*
*
* In the root process, the destination buffer can be the same as the source * buffer: *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+
 * |  1 |
 * |  2 |
 * +----+    +----+
 * |    |    |  3 |
 * |    |    |  4 |
 * +----+    +----+    +----+
 * |    |              |  5 |
 * |    |              |  6 |
 * +----+              +----+    +----+
 * |    |                        |  7 |
 * |    |                        |  8 |
 * +----+                        +----+
*
*
* After: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+
 * |  1 |
 * |  2 |
 * +----+    +----+
 * |  3 |    |  3 |
 * |  4 |    |  4 |
 * +----+    +----+    +----+
 * |  5 |              |  5 |
 * |  6 |              |  6 |
 * +----+              +----+    +----+
 * |  7 |                        |  7 |
 * |  8 |                        |  8 |
 * +----+                        +----+
*
*
* Note: Any process can be the root of the gather. The above is only * one example with process 0 as the root. *

* All-Gather *

* All-gather is the same as gather, except that every process has an array of * destination buffers, and every process receives the results of the gather. * Each process in the communicator has a source buffer (type {@linkplain * edu.rit.mp.Buf Buf}) filled with data. Each process in the communicator has * K destination buffers, where K is the size of the communicator. * For example, the destination buffers could be different portions of an array. * Each destination buffer has the same length and the same item data type as * the corresponding source buffer. Each process calls the communicator's * allGather() method. Afterwards, each destination buffer in every * process contains the same data as the corresponding source buffer. *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
 *
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |    |
 * |    |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |    |
 * |    |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |    |
 * |    |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |    |
 * |    |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
*
*
* After: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
 *
 * +----+    +----+    +----+    +----+
 * |  1 |    |  1 |    |  1 |    |  1 |
 * |  2 |    |  2 |    |  2 |    |  2 |
 * +----+    +----+    +----+    +----+
 * |  3 |    |  3 |    |  3 |    |  3 |
 * |  4 |    |  4 |    |  4 |    |  4 |
 * +----+    +----+    +----+    +----+
 * |  5 |    |  5 |    |  5 |    |  5 |
 * |  6 |    |  6 |    |  6 |    |  6 |
 * +----+    +----+    +----+    +----+
 * |  7 |    |  7 |    |  7 |    |  7 |
 * |  8 |    |  8 |    |  8 |    |  8 |
 * +----+    +----+    +----+    +----+
*
*
* The destination buffer can be the same as the source buffer in each process: *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |    |    |    |    |    |
 * |  2 |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
 * |    |    |  3 |    |    |    |    |
 * |    |    |  4 |    |    |    |    |
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |  5 |    |    |
 * |    |    |    |    |  6 |    |    |
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |  7 |
 * |    |    |    |    |    |    |  8 |
 * +----+    +----+    +----+    +----+
*
*
* After: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  1 |    |  1 |    |  1 |
 * |  2 |    |  2 |    |  2 |    |  2 |
 * +----+    +----+    +----+    +----+
 * |  3 |    |  3 |    |  3 |    |  3 |
 * |  4 |    |  4 |    |  4 |    |  4 |
 * +----+    +----+    +----+    +----+
 * |  5 |    |  5 |    |  5 |    |  5 |
 * |  6 |    |  6 |    |  6 |    |  6 |
 * +----+    +----+    +----+    +----+
 * |  7 |    |  7 |    |  7 |    |  7 |
 * |  8 |    |  8 |    |  8 |    |  8 |
 * +----+    +----+    +----+    +----+
*
*
*

* Reduce *

* Reduce is like gather, except the buffers' contents are combined together * instead of just copied. Each process in the communicator has a buffer (type * {@linkplain edu.rit.mp.Buf Buf}) filled with data. Each process calls the * communicator's reduce() method, specifying some binary operation * (type {@linkplain edu.rit.pj.reduction.Op Op}) for combining the data. * Afterwards, each element of the buffer in the root process contains the * result of combining all the corresponding elements in all the buffers using * the specified binary operation. For example, if the operation is addition, * each buffer element in the root process ends up being the sum of the * corresponding buffer elements in all the processes. In the non-root * processes, the buffers' contents may be changed from their original contents. *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
*
*
* After: * *
 * Process   Process   Process   Process
 * 0 (root)     1         2         3
 * +----+    +----+    +----+    +----+
 * | 16 |    | ?? |    | ?? |    | ?? |
 * | 20 |    | ?? |    | ?? |    | ?? |
 * +----+    +----+    +----+    +----+
*
*
* Note: Any process can be the root of the reduction. The above is only * one example with process 0 as the root. *

* All-Reduce *

* All-reduce is the same as reduce, except that every process receives the * results of the reduction. Each process in the communicator has a buffer (type * {@linkplain edu.rit.mp.Buf Buf}) filled with data. Each process calls the * communicator's allReduce() method, specifying some binary operation * (type {@linkplain edu.rit.pj.reduction.Op Op}) for combining the data. * Afterwards, each element of the buffer in each process contains the result of * combining all the corresponding elements in all the buffers using the * specified binary operation. For example, if the operation is addition, each * buffer element ends up being the sum of the corresponding buffer elements. *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
*
*
* After: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * | 16 |    | 16 |    | 16 |    | 16 |
 * | 20 |    | 20 |    | 20 |    | 20 |
 * +----+    +----+    +----+    +----+
*
*
*

* All-to-All *

* Every process in the communicator has K source buffers (type * {@linkplain edu.rit.mp.Buf Buf}) filled with data, where K is the size * of the communicator. Every process in the communicator also has K * destination buffers (type {@linkplain edu.rit.mp.Buf Buf}). The source * buffers and the destination buffers must refer to different storage. For * example, the source buffers could be portions of an array, and the * destination buffers could be portions of a different array. Each process * calls the communicator's allToAll() method. Afterwards, for each * process rank k, 0 <= k <= K-1, and each buffer * index i, 0 <= i <= K-1, destination buffer * i in process k contains the same data as source buffer k * in process i. *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  9 |    | 17 |    | 25 |
 * |  2 |    | 10 |    | 18 |    | 26 |
 * +----+    +----+    +----+    +----+
 * |  3 |    | 11 |    | 19 |    | 27 |
 * |  4 |    | 12 |    | 20 |    | 28 |
 * +----+    +----+    +----+    +----+
 * |  5 |    | 13 |    | 21 |    | 29 |
 * |  6 |    | 14 |    | 22 |    | 30 |
 * +----+    +----+    +----+    +----+
 * |  7 |    | 15 |    | 23 |    | 31 |
 * |  8 |    | 16 |    | 24 |    | 32 |
 * +----+    +----+    +----+    +----+
 *
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |    |
 * |    |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |    |
 * |    |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |    |
 * |    |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
 * |    |    |    |    |    |    |    |
 * |    |    |    |    |    |    |    |
 * +----+    +----+    +----+    +----+
*
*
* After: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  9 |    | 17 |    | 25 |
 * |  2 |    | 10 |    | 18 |    | 26 |
 * +----+    +----+    +----+    +----+
 * |  3 |    | 11 |    | 19 |    | 27 |
 * |  4 |    | 12 |    | 20 |    | 28 |
 * +----+    +----+    +----+    +----+
 * |  5 |    | 13 |    | 21 |    | 29 |
 * |  6 |    | 14 |    | 22 |    | 30 |
 * +----+    +----+    +----+    +----+
 * |  7 |    | 15 |    | 23 |    | 31 |
 * |  8 |    | 16 |    | 24 |    | 32 |
 * +----+    +----+    +----+    +----+
 *
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
 * |  9 |    | 11 |    | 13 |    | 15 |
 * | 10 |    | 12 |    | 14 |    | 16 |
 * +----+    +----+    +----+    +----+
 * | 17 |    | 19 |    | 21 |    | 23 |
 * | 18 |    | 20 |    | 22 |    | 24 |
 * +----+    +----+    +----+    +----+
 * | 25 |    | 27 |    | 29 |    | 31 |
 * | 26 |    | 28 |    | 30 |    | 32 |
 * +----+    +----+    +----+    +----+
*
*
*

* Scan *

* Each process in the communicator has a buffer (type {@linkplain * edu.rit.mp.Buf Buf}) filled with data. Each process calls the communicator's * scan() method, specifying some binary operation (type {@linkplain * edu.rit.pj.reduction.Op Op}) for combining the data. Afterwards, each element * of the buffer in a particular process contains the result of combining all * the corresponding elements in its own and all lower-ranked processes' buffers * using the specified binary operation. For example, if the operation is * addition, each buffer element ends up being the sum of its own and all * lower-ranked processes' buffer elements. *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
*
*
* After: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  4 |    |  9 |    | 16 |
 * |  2 |    |  6 |    | 12 |    | 20 |
 * +----+    +----+    +----+    +----+
*
*
* The scan operation is also known as "prefix scan" or "inclusive prefix scan" * -- "inclusive" because the process's own element is included in the result. *

* Exclusive-Scan *

* The exclusive-scan operation is a variation of the scan operation. Each * process in the communicator has a buffer (type {@linkplain edu.rit.mp.Buf * Buf}) filled with data. Each process calls the communicator's * exclusiveScan() method, specifying some binary operation (type * {@linkplain edu.rit.pj.reduction.Op Op}) for combining the data, and * specifying an initial data value. Afterwards, each element of the buffer in a * particular process contains the result of combining all the corresponding * elements in all lower-ranked processes' buffers using the specified binary * operation, except in process 0 each element of the buffer contains the * initial data value. For example, if the operation is addition and the initial * data value is 0, each buffer element ends up being the sum of all * lower-ranked processes' buffer elements. *

*

* * * * * *
* Before: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  1 |    |  3 |    |  5 |    |  7 |
 * |  2 |    |  4 |    |  6 |    |  8 |
 * +----+    +----+    +----+    +----+
*
*
* After: * *
 * Process   Process   Process   Process
 *    0         1         2         3
 * +----+    +----+    +----+    +----+
 * |  0 |    |  1 |    |  4 |    |  9 |
 * |  0 |    |  2 |    |  6 |    | 12 |
 * +----+    +----+    +----+    +----+
*
*
* This version of the scan operation is also known as "exclusive prefix scan" * -- "exclusive" because the process's own element is excluded from the result. *

* Barrier *

* The barrier operation causes all the processes to synchronize with each * other. Each process calls the communicator's barrier() method. The * calling thread blocks until all processes in the communicator have called the * barrier() method. Then the calling thread unblocks and returns from * the barrier() method call. * * @author Alan Kaminsky * @version 21-Jan-2009 */ public class Comm { // Hidden data members. // Predefined communicators. private static Comm theWorldCommunicator; private static Comm theFrontendCommunicator; // This communicator's size, rank, and host. private int mySize; private int myRank; private String myHost; // The largest power of 2 less than or equal to this communicator's size. private int mySizePowerOf2; // Channel group for message passing in this communicator. private ChannelGroup myChannelGroup; // Map from rank (array index) to channel group address (array element). private InetSocketAddress[] myAddressForRank; // Map from rank (array index) to channel for communicating with the process // at that rank (array element). private Channel[] myChannelForRank; // Broadcast trees for flood-send, flood-receive, broadcast, and reduce // operations, indexed by root. private int[][] myBroadcastTree; // Hidden constructors. /** * Construct a new communicator. * * @param size * Communicator's size. * @param rank * Current process's rank in the communicator. * @param host * Host name. * @param channelgroup * Channel group for message passing in this communicator. * @param address * Map from rank (array index) to channel group address (array element). */ private Comm (int size, int rank, String host, ChannelGroup channelgroup, InetSocketAddress[] address) { // Record size, rank, channel group. mySize = size; myRank = rank; myHost = host; myChannelGroup = channelgroup; // Determine the largest power of 2 less than or equal to this // communicator's size. int p2 = 1; while (p2 <= size) p2 <<= 1; mySizePowerOf2 = p2 >>> 1; // Set channel group ID equal to rank. myChannelGroup.setChannelGroupId (rank); // Set up connect listener. myChannelGroup.setConnectListener (new ConnectListener() { public void nearEndConnected (ChannelGroup theChannelGroup, Channel theChannel) throws IOException { } public void farEndConnected (ChannelGroup theChannelGroup, Channel theChannel) throws IOException { doFarEndConnected (theChannel); } }); // Record socket address for each process rank. myAddressForRank = address; // Set up channel for each process rank. myChannelForRank = new Channel [size]; // Populate channel at my own rank with the loopback channel. myChannelForRank[myRank] = channelgroup.loopbackChannel(); // If there's more than one process, start listening for incoming // connections. if (mySize > 1) myChannelGroup.startListening(); } // Exported operations. /** * Initialize the PJ message passing middleware. Certain Java system * properties specify the middleware's behavior; these properties are * typically given on the Java command line with the "-D" flag. * For further information, see class {@linkplain PJProperties}. * * @param args Command line arguments. * * @exception NullPointerException * (unchecked exception) Thrown if args is null. * @exception IllegalArgumentException * (unchecked exception) Thrown if the value of one of the Java system * properties is illegal. * @exception IOException * Thrown if an I/O error occurred. */ public static void init (String[] args) throws IOException { // Verify preconditions. if (args == null) { throw new NullPointerException ("Comm.init(): args is null"); } // Get the job backend object. JobBackend backend = JobBackend.getJobBackend(); if (backend == null) { // We're running on the frontend processor. // Prepare constructor arguments for the Job Frontend object. String username = System.getProperty ("user.name"); int Nn = PJProperties.getPjNn(); int Np = PJProperties.getPjNp(); int Nt = PJProperties.getPjNt(); boolean hasFrontendComm = false; // Examine the call stack to find the main program class name. StackTraceElement[] stack = Thread.currentThread().getStackTrace(); StackTraceElement bottom = stack[stack.length-1]; if (! bottom.getMethodName().equals ("main")) { throw new IllegalStateException ("Comm.init(): Not called from main program"); } String mainClassName = bottom.getClassName(); // Set up the Job Frontend object. JobFrontend frontend = null; try { frontend = new JobFrontend (username, Nn, Np, Nt, hasFrontendComm, mainClassName, args); // We were able to contact the Job Scheduler. // Run the job frontend in this process, then exit. frontend.run(); System.exit (0); } catch (JobSchedulerException exc) { // We were not able to contact the Job Scheduler. System.err.println ("No Job Scheduler at " + PJProperties.getPjHost() + ":" + PJProperties.getPjPort() + ", running in this (one) process"); // Set up world communicator. theWorldCommunicator = new Comm (/*size */ 1, /*rank */ 0, /*host */ "", /*channelgroup*/ new ChannelGroup(), /*address */ new InetSocketAddress[] {new InetSocketAddress (0)}); } } else { // We're running on a backend processor. // Set up world communicator. theWorldCommunicator = new Comm (/*size */ backend.getK(), /*rank */ backend.getRank(), /*host */ backend.getBackendHost(), /*channelgroup*/ backend.getWorldChannelGroup(), /*address */ backend.getWorldAddress()); } } /** * Obtain a reference to the world communicator. * * @return World communicator. * * @exception IllegalStateException * (unchecked exception) Thrown if Comm.init() has not been * called. Thrown if world() is called in the job frontend * process; the world communicator does not exist in the job frontend * process. */ public static Comm world() { if (theWorldCommunicator != null) { return theWorldCommunicator; } else if (JobBackend.getJobBackend() != null) { throw new IllegalStateException ("Comm.world(): Didn't call Comm.init()"); } else { throw new IllegalStateException ("Comm.world(): World communicator doesn't exist in job frontend process"); } } /** * Obtain the number of processes in this communicator. * * @return Size. */ public int size() { return mySize; } /** * Obtain the current process's rank in this communicator. * * @return Rank. */ public int rank() { return myRank; } /** * Obtain the host name of this communicator's backend processor. If this * communicator is not running on a cluster backend processor, the host name * is "<unknown>". * * @return Host name. */ public String host() { return myHost; } /** * Create a new communicator. Every process in this communicator must * call the createComm() method. Each process passes true or false * for the participate argument to state whether the process will * participate in the new communicator. At least one process must * participate in the new communicator. Messages to set up the new * communicator are sent to all processes in this communicator, using a * message tag of 0. *

* In processes participating in the new communicator, the new communicator * is returned. The participating processes appear in the same order by rank * in the new communicator as in this communicator. The process can call the * new communicator's rank() method to determine the process's rank * in the new communicator. *

* In processes not participating in the new communicator, null is returned. * * @param participate True if this process will participate in the new * communicator; false otherwise. * * @return New communicator if this process will participate in the new * communicator; null otherwise. * * @exception IOException * Thrown if an I/O error occurred. */ public Comm createComm (boolean participate) throws IOException { return createComm (participate, 0); } /** * Create a new communicator. Every process in this communicator must * call the createComm() method. Each process passes true or false * for the participate argument to state whether the process will * participate in the new communicator. At least one process must * participate in the new communicator. Messages to set up the new * communicator are sent to all processes in this communicator, using the * given message tag. *

* In processes participating in the new communicator, the new communicator * is returned. The participating processes appear in the same order by rank * in the new communicator as in this communicator. The process can call the * new communicator's rank() method to determine the process's rank * in the new communicator. *

* In processes not participating in the new communicator, null is returned. * * @param participate True if this process will participate in the new * communicator; false otherwise. * @param tag Message tag. * * @return New communicator if this process will participate in the new * communicator; null otherwise. * * @exception IOException * Thrown if an I/O error occurred. */ public Comm createComm (boolean participate, int tag) throws IOException { // Set up array of socket addresses for all processes. InetSocketAddress[] address = new InetSocketAddress [mySize]; ObjectBuf[] addressbuf = ObjectBuf.sliceBuffers (address, new Range (0, mySize-1) .subranges (mySize)); // Create channel group for new communicator, if participating. ChannelGroup channelgroup = null; InetSocketAddress myaddress = null; if (participate) { channelgroup = new ChannelGroup (new InetSocketAddress (myChannelGroup.listenAddress().getAddress(), 0)); myaddress = channelgroup.listenAddress(); address[myRank] = myaddress; } // All-gather channel group socket addresses into every process. allGather (tag, addressbuf[myRank], addressbuf); // Close up gaps in the socket address array if any. int off = 0; int newsize = 0; int newrank = -1; for (int i = 0; i < mySize; ++ i) { if (address[i] == null) { ++ off; } else { if (i == myRank) newrank = i-off; address[i-off] = address[i]; ++ newsize; } } // Verify size of new communicator. if (newsize == 0) { throw new IOException ("Comm.createComm(): No processes in communicator"); } // Return new communicator if participating. if (participate) { return new Comm (newsize, newrank, myHost, channelgroup, address); } // Return null if not participating. else { return null; } } /** * Send a message to the process at the given rank in this communicator. The * message uses a tag of 0. The message items come from the given buffer. To * receive the message, the destination process must call the * receive() method. When the send() method returns, the * message has been fully sent, but it may not yet have been fully received. *

* A process can send a message to itself; in this case a different thread * must call the receive() method on this communicator. * * @param toRank Destination process's rank in this communicator. * @param buffer Buffer of data items to be sent. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if toRank is not in the range 0 * .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public void send (int toRank, Buf buffer) throws IOException { send (toRank, 0, buffer); } /** * Send a message to the process at the given rank in this communicator with * the given message tag. The message items come from the given buffer. To * receive the message, the destination process must call the * receive() method. When the send() method returns, the * message has been fully sent, but it may not yet have been fully received. *

* A process can send a message to itself; in this case a different thread * must call the receive() method on this communicator. * * @param toRank Destination process's rank in this communicator. * @param tag Message tag. * @param buffer Buffer of data items to be sent. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if toRank is not in the range 0 * .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public void send (int toRank, int tag, Buf buffer) throws IOException { myChannelGroup.send (getChannel (toRank), tag, buffer); } /** * Send a message to the process at the given rank in this communicator * (non-blocking). A message tag of 0 is used. The message items come from * the given buffer. To receive the message, the destination process must * call the receive() method. *

* The send() method initiates the send operation and immediately * returns a {@linkplain CommRequest} object. The send operation is * performed by a separate thread. To wait for the send operation to finish, * call the returned {@linkplain CommRequest} object's * waitForFinish() method. When that method returns, the message * has been fully sent, but it may not yet have been fully received. *

* A process can send a message to itself; in this case a different thread * must call the receive() method on this communicator. * * @param toRank Destination process's rank in this communicator. * @param buffer Buffer of data items to be sent. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if toRank is not in the range 0 * .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest send (int toRank, Buf buffer, CommRequest request) throws IOException { return send (toRank, 0, buffer, request); } /** * Send a message to the process at the given rank in this communicator with * the given message tag (non-blocking). The message items come from the * given buffer. To receive the message, the destination process must call * the receive() method. *

* The send() method initiates the send operation and immediately * returns a {@linkplain CommRequest} object. The send operation is * performed by a separate thread. To wait for the send operation to finish, * call the returned {@linkplain CommRequest} object's * waitForFinish() method. When that method returns, the message * has been fully sent, but it may not yet have been fully received. *

* A process can send a message to itself; in this case a different thread * must call the receive() method on this communicator. * * @param toRank Destination process's rank in this communicator. * @param tag Message tag. * @param buffer Buffer of data items to be sent. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if toRank is not in the range 0 * .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest send (int toRank, int tag, Buf buffer, CommRequest request) throws IOException { // Set up CommRequest object. CommRequest req = request == null ? new CommRequest() : request; // Send message (non-blocking). req.mySendRequest = new IORequest(); req.myRecvRequest = null; myChannelGroup.sendNoWait (getChannel (toRank), tag, buffer, req.mySendRequest); // Return CommRequest object. return req; } /** * Receive a message from the process at the given rank in this * communicator. If rank is null, a message will be received from * any process in this communicator. The message must have a tag of 0. The * received message items are stored in the given buffer. To send the * message, the source process must call the send() method. When * the receive() method returns, the message has been fully * received. *

* A {@linkplain CommStatus} object is returned. The status object gives the * actual rank of the process that sent the message, the actual message tag * that was received, and the actual number of data items in the message. If * the actual number of data items in the message is less than the length of * the buffer, nothing is stored into the extra data items at the end of the * buffer. If the actual number of data items in the message is greater than * the length of the buffer, the extra data items at the end of the message * are discarded. *

* A process can receive a message from itself; in this case a different * thread must call the send() method on this communicator. * * @param fromRank Source process's rank in this communicator, or null to * receive from any process. * @param buffer Buffer of data items to be received. * * @return Status object giving the outcome of the message reception. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if fromRank is not null and is * not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommStatus receive (Integer fromRank, Buf buffer) throws IOException { return receive (fromRank, 0, buffer); } /** * Receive a message from the process at the given rank in this communicator * with the given message tag. If rank is null, a message will be * received from any process in this communicator. The received message * items are stored in the given buffer. To send the message, the source * process must call the send() method. When the receive() * method returns, the message has been fully received. *

* A {@linkplain CommStatus} object is returned. The status object gives the * actual rank of the process that sent the message, the actual message tag * that was received, and the actual number of data items in the message. If * the actual number of data items in the message is less than the length of * the buffer, nothing is stored into the extra data items at the end of the * buffer. If the actual number of data items in the message is greater than * the length of the buffer, the extra data items at the end of the message * are discarded. *

* A process can receive a message from itself; in this case a different * thread must call the send() method on this communicator. * * @param fromRank Source process's rank in this communicator, or null to * receive from any process. * @param tag Message tag. * @param buffer Buffer of data items to be received. * * @return Status object giving the outcome of the message reception. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if fromRank is not null and is * not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommStatus receive (Integer fromRank, int tag, Buf buffer) throws IOException { Status status; // If source is a wildcard, ensure a channel to every process, then // receive from any process. if (fromRank == null) { for (int src = 0; src < mySize; ++ src) ensureChannel (src); status = myChannelGroup.receive (null, tag, buffer); } // If source is not a wildcard, receive from that process. else { status = myChannelGroup.receive (getChannel (fromRank), tag, buffer); } // Return CommStatus object. return new CommStatus (getFarRank (status.channel), status.tag, status.length); } /** * Receive a message from the process at the given rank in this communicator * with the given message tag range. If rank is null, a message * will be received from any process in this communicator. If * tagRange is null, a message will be received with any tag. If * tagRange is not null, a message will be received with any tag in * the given range. The received message items are stored in the given * buffer. To send the message, the source process must call the * send() method. When the receive() method returns, the * message has been fully received. *

* A {@linkplain CommStatus} object is returned. The status object gives the * actual rank of the process that sent the message, the actual message tag * that was received, and the actual number of data items in the message. If * the actual number of data items in the message is less than the length of * the buffer, nothing is stored into the extra data items at the end of the * buffer. If the actual number of data items in the message is greater than * the length of the buffer, the extra data items at the end of the message * are discarded. *

* A process can receive a message from itself; in this case a different * thread must call the send() method on this communicator. * * @param fromRank Source process's rank in this communicator, or null to * receive from any process. * @param tagRange Message tag range, or null to receive any tag. * @param buffer Buffer of data items to be received. * * @return Status object giving the outcome of the message reception. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if fromRank is not null and is * not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommStatus receive (Integer fromRank, Range tagRange, Buf buffer) throws IOException { Status status; // If source is a wildcard, ensure a channel to every process, then // receive from any process. if (fromRank == null) { for (int src = 0; src < mySize; ++ src) ensureChannel (src); status = myChannelGroup.receive (null, tagRange, buffer); } // If source is not a wildcard, receive from that process. else { status = myChannelGroup.receive (getChannel (fromRank), tagRange, buffer); } // Return CommStatus object. return new CommStatus (getFarRank (status.channel), status.tag, status.length); } /** * Receive a message from the process at the given rank in this communicator * (non-blocking). If rank is null, a message will be received from * any process in this communicator. The message must have a tag of 0. The * received message items are stored in the given buffer. To send the * message, the source process must call the send() method. *

* The receive() method initiates the receive operation and * immediately returns a {@linkplain CommRequest} object. The receive * operation is performed by a separate thread. To wait for the receive * operation to finish, call the returned {@linkplain CommRequest} object's * waitForFinish() method. When that method returns, the incoming * message items have been fully received. *

* A process can receive a message from itself; in this case a different * thread must call the send() method on this communicator. * * @param fromRank Source process's rank in this communicator, or null to * receive from any process. * @param buffer Buffer of data items to be received. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if fromRank is not null and is * not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest receive (Integer fromRank, Buf buffer, CommRequest request) throws IOException { return receive (fromRank, 0, buffer, request); } /** * Receive a message from the process at the given rank in this communicator * with the given message tag (non-blocking). If rank is null, a * message will be received from any process in this communicator. If * tag is null, a message will be received with any tag. The * received message items are stored in the given buffer. To send the * message, the source process must call the send() method. *

* The receive() method initiates the receive operation and * immediately returns a {@linkplain CommRequest} object. The receive * operation is performed by a separate thread. To wait for the receive * operation to finish, call the returned {@linkplain CommRequest} object's * waitForFinish() method. When that method returns, the incoming * message items have been fully received. *

* A process can receive a message from itself; in this case a different * thread must call the send() method on this communicator. * * @param fromRank Source process's rank in this communicator, or null to * receive from any process. * @param tag Message tag. * @param buffer Buffer of data items to be received. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if fromRank is not null and is * not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest receive (Integer fromRank, int tag, Buf buffer, CommRequest request) throws IOException { // Set up CommRequest object. CommRequest req = request == null ? new CommRequest() : request; req.mySendRequest = null; req.myRecvRequest = new IORequest(); // If source is a wildcard, ensure a channel to every process, then // receive (non-blocking) from any process. if (fromRank == null) { for (int src = 0; src < mySize; ++ src) ensureChannel (src); myChannelGroup.receiveNoWait (null, tag, buffer, req.myRecvRequest); } // If source is not a wildcard, receive (non-blocking) from that // process. else { myChannelGroup.receiveNoWait (getChannel (fromRank), tag, buffer, req.myRecvRequest); } // Return CommRequest object. return req; } /** * Receive a message from the process at the given rank in this communicator * with the given message tag range (non-blocking). If rank is * null, a message will be received from any process in this communicator. * If tagRange is null, a message will be received with any tag. If * tagRange is not null, a message will be received with any tag in * the given range. The received message items are stored in the given * buffer. To send the message, the source process must call the * send() method. *

* The receive() method initiates the receive operation and * immediately returns a {@linkplain CommRequest} object. The receive * operation is performed by a separate thread. To wait for the receive * operation to finish, call the returned {@linkplain CommRequest} object's * waitForFinish() method. When that method returns, the incoming * message items have been fully received. *

* A process can receive a message from itself; in this case a different * thread must call the send() method on this communicator. * * @param fromRank Source process's rank in this communicator, or null to * receive from any process. * @param tagRange Message tag range, or null to receive any tag. * @param buffer Buffer of data items to be received. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if fromRank is not null and is * not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest receive (Integer fromRank, Range tagRange, Buf buffer, CommRequest request) throws IOException { // Set up CommRequest object. CommRequest req = request == null ? new CommRequest() : request; req.mySendRequest = null; req.myRecvRequest = new IORequest(); // If source is a wildcard, ensure a channel to every process, then // receive (non-blocking) from any process. if (fromRank == null) { for (int src = 0; src < mySize; ++ src) ensureChannel (src); myChannelGroup.receiveNoWait (null, tagRange, buffer, req.myRecvRequest); } // If source is not a wildcard, receive (non-blocking) from that // process. else { myChannelGroup.receiveNoWait (getChannel (fromRank), tagRange, buffer, req.myRecvRequest); } // Return CommRequest object. return req; } /** * Send a message to the process at the given rank in this communicator, and * receive a message from the process at the given rank in this * communicator. A message tag of 0 is used. The outgoing message items come * from the buffer sendbuf. The incoming message items go into the * buffer recvbuf. The outgoing message items must come from a * different place than where the incoming message items will be stored. The * destination process (process toRank) must call a method to * receive this process's outgoing message items. The source process * (process fromRank) must call a method to send this process's * incoming message items. When the sendReceive() method returns, * the outgoing message items have been fully sent, but they may not yet * have been fully received; and the incoming message items have been fully * received. *

* A {@linkplain CommStatus} object is returned giving the results of the * receive half of the operation. The status object gives the rank of the * process that sent the incoming message, the message tag that was * received, and the actual number of data items in the message. If the * actual number of data items in the message is less than the length of the * receive buffer, nothing is stored into the extra data items at the end of * the receive buffer. If the actual number of data items in the message is * greater than the length of the receive buffer, the extra data items at * the end of the message are discarded. *

* A process can send-receive messages with itself; in this case a different * thread must call the sendReceive() method on this communicator. * * @param toRank Destination process's rank in this communicator. * @param sendBuf Buffer of data items to be sent. * @param fromRank Source process's rank in this communicator. * @param recvBuf Buffer of data items to be received. * * @return Status object giving the outcome of the message reception. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if toRank or fromRank * is not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if sendBuf or recvBuf * is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommStatus sendReceive (int toRank, Buf sendBuf, int fromRank, Buf recvBuf) throws IOException { return sendReceive (toRank, 0, sendBuf, fromRank, 0, recvBuf); } /** * Send a message to the process at the given rank in this communicator with * the given message tag, and receive a message from the process at the * given rank in this communicator with the given message tag. The outgoing * message items come from the buffer sendbuf. The incoming message * items go into the buffer recvbuf. The outgoing message items * must come from a different place than where the incoming message items * will be stored. The destination process (process toRank) must * call a method to receive this process's outgoing message items. The * source process (process fromRank) must call a method to send * this process's incoming message items. When the sendReceive() * method returns, the outgoing message items have been fully sent, but they * may not yet have been fully received; and the incoming message items have * been fully received. *

* A {@linkplain CommStatus} object is returned giving the results of the * receive half of the operation. The status object gives the rank of the * process that sent the incoming message, the message tag that was * received, and the actual number of data items in the message. If the * actual number of data items in the message is less than the length of the * receive buffer, nothing is stored into the extra data items at the end of * the receive buffer. If the actual number of data items in the message is * greater than the length of the receive buffer, the extra data items at * the end of the message are discarded. *

* A process can send-receive messages with itself; in this case a different * thread must call the sendReceive() method on this communicator. * * @param toRank Destination process's rank in this communicator. * @param sendTag Message tag for outgoing message. * @param sendBuf Buffer of data items to be sent. * @param fromRank Source process's rank in this communicator. * @param recvTag Message tag for incoming message. * @param recvBuf Buffer of data items to be received. * * @return Status object giving the outcome of the message reception. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if toRank or fromRank * is not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if sendBuf or recvBuf * is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommStatus sendReceive (int toRank, int sendTag, Buf sendBuf, int fromRank, int recvTag, Buf recvBuf) throws IOException { // Send the outgoing message (non-blocking). IORequest sendRequest = new IORequest(); myChannelGroup.sendNoWait (getChannel (toRank), sendTag, sendBuf, sendRequest); // Receive the outgoing message (non-blocking). IORequest recvRequest = new IORequest(); myChannelGroup.receiveNoWait (getChannel (fromRank), recvTag, recvBuf, recvRequest); // Wait for both messages to finish. sendRequest.waitForFinish(); Status status = recvRequest.waitForFinish(); return new CommStatus (getFarRank (status.channel), status.tag, status.length); } /** * Send a message to the process at the given rank in this communicator, and * receive a message from the process at the given rank in this communicator * (non-blocking). A message tag of 0 is used. The outgoing message items * come from the buffer sendbuf. The incoming message items go into * the buffer recvbuf. The outgoing message items must come from a * different place than where the incoming message items will be stored. The * destination process (process toRank) must call a method to * receive this process's outgoing message items. The source process * (process fromRank) must call a method to send this process's * incoming message items. *

* The sendReceive() method initiates the send and receive * operations and immediately returns a {@linkplain CommRequest} object. The * send and receive operations are performed by a separate thread. To wait * for the send and receive operations to finish, call the returned * {@linkplain CommRequest} object's waitForFinish() method. When * that method returns, the outgoing message items have been fully sent, but * they may not yet have been fully received; and the incoming message items * have been fully received. *

* A process can send-receive messages with itself; in this case a different * thread must call the sendReceive() method on this communicator. * * @param toRank Destination process's rank in this communicator. * @param sendBuf Buffer of data items to be sent. * @param fromRank Source process's rank in this communicator. * @param recvBuf Buffer of data items to be received. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if toRank or fromRank * is not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if sendBuf or recvBuf * is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest sendReceive (int toRank, Buf sendBuf, int fromRank, Buf recvBuf, CommRequest request) throws IOException { return sendReceive (toRank, 0, sendBuf, fromRank, 0, recvBuf, request); } /** * Send a message to the process at the given rank in this communicator with * the given message tag, and receive a message from the process at the * given rank in this communicator with the given message tag * (non-blocking). The outgoing message items come from the buffer * sendbuf. The incoming message items go into the buffer * recvbuf. The outgoing message items must come from a different * place than where the incoming message items will be stored. The * destination process (process toRank) must call a method to * receive this process's outgoing message items. The source process * (process fromRank) must call a method to send this process's * incoming message items. *

* The sendReceive() method initiates the send and receive * operations and immediately returns a {@linkplain CommRequest} object. The * send and receive operations are performed by a separate thread. To wait * for the send and receive operations to finish, call the returned * {@linkplain CommRequest} object's waitForFinish() method. When * that method returns, the outgoing message items have been fully sent, but * they may not yet have been fully received; and the incoming message items * have been fully received. *

* A process can send-receive messages with itself; in this case a different * thread must call the sendReceive() method on this communicator. * * @param toRank Destination process's rank in this communicator. * @param sendTag Message tag for outgoing message. * @param sendBuf Buffer of data items to be sent. * @param fromRank Source process's rank in this communicator. * @param recvTag Message tag for incoming message. * @param recvBuf Buffer of data items to be received. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if toRank or fromRank * is not in the range 0 .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if sendBuf or recvBuf * is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest sendReceive (int toRank, int sendTag, Buf sendBuf, int fromRank, int recvTag, Buf recvBuf, CommRequest request) throws IOException { // Set up CommRequest object. CommRequest req = request == null ? new CommRequest() : request; // Send the outgoing message (non-blocking). req.mySendRequest = new IORequest(); myChannelGroup.sendNoWait (getChannel (toRank), sendTag, sendBuf, req.mySendRequest); // Receive the outgoing message (non-blocking). req.myRecvRequest = new IORequest(); myChannelGroup.receiveNoWait (getChannel (fromRank), recvTag, recvBuf, req.myRecvRequest); // Return CommRequest object. return req; } /** * Flood-send a message to all processes in this communicator. The message * uses a tag of 0. The message items come from the given buffer. To receive * the message, every process (including the sending process) must call the * floodReceive() method. When the floodSend() method * returns, the message has been fully sent, but it may not yet have been * fully received in all processes. *

* Note: The length of the incoming buffer in the * floodReceive() method call must be the same as the length of the * outgoing buffer in the floodSend() method call. * * @param buffer Buffer of data items to be sent. * * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public void floodSend (Buf buffer) throws IOException { floodSend (0, buffer, null) .waitForFinish(); } /** * Flood-send a message to all processes in this communicator with the given * message tag. The message items come from the given buffer. To receive the * message, every process (including the sending process) must call the * floodReceive() method. When the floodSend() method * returns, the message has been fully sent, but it may not yet have been * fully received in all processes. *

* Note: The length of the incoming buffer in the * floodReceive() method call must be the same as the length of the * outgoing buffer in the floodSend() method call. * * @param tag Message tag. * @param buffer Buffer of data items to be sent. * * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public void floodSend (int tag, Buf buffer) throws IOException { floodSend (tag, buffer, null) .waitForFinish(); } /** * Flood-send a message to all processes in this communicator * (non-blocking). A message tag of 0 is used. The message items come from * the given buffer. To receive the message, every process (including the * sending process) must call the floodReceive() method. *

* The floodSend() method initiates the flood-send operation and * immediately returns a {@linkplain CommRequest} object. The flood-send * operation is performed by a separate thread. To wait for the flood-send * operation to finish, call the returned {@linkplain CommRequest} object's * waitForFinish() method. When that method returns, the message * has been fully sent, but it may not yet have been fully received in all * processes. *

* Note: The length of the incoming buffer in the * floodReceive() method call must be the same as the length of the * outgoing buffer in the floodSend() method call. * * @param buffer Buffer of data items to be sent. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest floodSend (Buf buffer, CommRequest request) throws IOException { return floodSend (0, buffer, request); } /** * Flood-send a message to all processes in this communicator with the given * message tag (non-blocking). The message items come from the given buffer. * To receive the message, every process (including the sending process) * must call the floodReceive() method. *

* The floodSend() method initiates the flood-send operation and * immediately returns a {@linkplain CommRequest} object. The flood-send * operation is performed by a separate thread. To wait for the flood-send * operation to finish, call the returned {@linkplain CommRequest} object's * waitForFinish() method. When that method returns, the message * has been fully sent, but it may not yet have been fully received in all * processes. *

* Note: The length of the incoming buffer in the * floodReceive() method call must be the same as the length of the * outgoing buffer in the floodSend() method call. * * @param tag Message tag. * @param buffer Buffer of data items to be sent. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest floodSend (int tag, Buf buffer, CommRequest request) throws IOException { // Set up CommRequest object. CommRequest req = request == null ? new CommRequest() : request; req.mySendRequest = new IORequest(); req.myRecvRequest = null; // Send data to process 0. Process 0's flood-receive I/O request object // will forward the data to all the processes. myChannelGroup.sendNoWait (getChannel (0), tag, buffer, req.mySendRequest); // Return CommRequest object. return req; } /** * Flood-receive a message from any process in this communicator. The * message must have a tag of 0. The received message items are stored in * the given buffer. To send the message, the source process must call the * floodSend() method. When the floodReceive() method * returns, the message has been fully received. *

* A {@linkplain CommStatus} object is returned. The status object gives the * actual rank of the process that sent the message, the actual message tag * that was received, and the actual number of data items in the message. *

* Note: The length of the incoming buffer in the * floodReceive() method call must be the same as the length of the * outgoing buffer in the floodSend() method call. * * @param buffer Buffer of data items to be received. * * @return Status object giving the outcome of the message reception. * * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommStatus floodReceive (Buf buffer) throws IOException { return floodReceive (0, buffer, null) .waitForFinish(); } /** * Flood-receive a message from any process in this communicator with the * given message tag. If tag is null, a message will be received * with any tag. The received message items are stored in the given buffer. * To send the message, the source process must call the * floodSend() method. When the floodReceive() method * returns, the message has been fully received. *

* A {@linkplain CommStatus} object is returned. The status object gives the * actual rank of the process that sent the message, the actual message tag * that was received, and the actual number of data items in the message. *

* Note: The length of the incoming buffer in the * floodReceive() method call must be the same as the length of the * outgoing buffer in the floodSend() method call. * * @param tag Message tag, or null to receive any tag. * @param buffer Buffer of data items to be received. * * @return Status object giving the outcome of the message reception. * * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommStatus floodReceive (Integer tag, Buf buffer) throws IOException { return floodReceive (tag, buffer, null) .waitForFinish(); } /** * Flood-receive a message from any process in this communicator * (non-blocking). A message tag of 0 is used. If tag is null, a * message will be received with any tag. The received message items are * stored in the given buffer. To send the message, the source process must * call the floodSend() method. *

* The floodReceive() method initiates the flood-receive operation * and immediately returns a {@linkplain CommRequest} object. The * flood-receive operation is performed by a separate thread. To wait for * the flood-receive operation to finish, call the returned {@linkplain * CommRequest} object's waitForFinish() method. When that method * returns, the incoming message items have been fully received. *

* Note: The length of the incoming buffer in the * floodReceive() method call must be the same as the length of the * outgoing buffer in the floodSend() method call. * * @param buffer Buffer of data items to be received. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest floodReceive (Buf buffer, CommRequest request) throws IOException { return floodReceive (0, buffer, request); } /** * Flood-receive a message from any process in this communicator with the * given message tag (non-blocking). If tag is null, a message will * be received with any tag. The received message items are stored in the * given buffer. To send the message, the source process must call the * floodSend() method. *

* The floodReceive() method initiates the flood-receive operation * and immediately returns a {@linkplain CommRequest} object. The * flood-receive operation is performed by a separate thread. To wait for * the flood-receive operation to finish, call the returned {@linkplain * CommRequest} object's waitForFinish() method. When that method * returns, the incoming message items have been fully received. *

* Note: The length of the incoming buffer in the * floodReceive() method call must be the same as the length of the * outgoing buffer in the floodSend() method call. * * @param tag Message tag, or null to receive any tag. * @param buffer Buffer of data items to be received. * @param request CommRequest object to use to wait for the operation to * finish; in this case request is returned. If * request is null, a new CommRequest object is * created and returned. * * @return CommRequest object to use to wait for the operation to finish. * * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public CommRequest floodReceive (Integer tag, Buf buffer, CommRequest request) throws IOException { // Get broadcast tree for root=0. int[] tree = getBroadcastTree (0); // Set up CommRequest object with a special I/O request object that // forwards the message down the broadcast tree. CommRequest req = request == null ? new CommRequest() : request; req.mySendRequest = null; req.myRecvRequest = new FloodReceiveIORequest (tree); // In process 0, ensure a channel to every process, then receive // (non-blocking) a message from any process. if (myRank == 0) { for (int src = 0; src < mySize; ++ src) ensureChannel (src); myChannelGroup.receiveNoWait (null, tag, buffer, req.myRecvRequest); } // In other processes, ensure a channel to the child processes in the // broadcast tree, then receive (non-blocking) a message from the parent // process in the broadcast tree. else { for (int i = 1; i < tree.length; ++ i) ensureChannel (tree[i]); myChannelGroup.receiveNoWait (getChannel (tree[0]), tag, buffer, req.myRecvRequest); } // Return CommRequest object. return req; } /** * Class FloodReceiveIORequest overrides the methods of class IORequest with * additional processing to forward the message when a message is received. */ private class FloodReceiveIORequest extends IORequest { // Broadcast tree. private int[] tree; // List of zero or more additional I/O requests to forward copies of the // received message. private LinkedList myForwardedIORequests = new LinkedList(); /** * Construct a new I/O request object. * * @param tree Broadcast tree. */ public FloodReceiveIORequest (int[] tree) { super(); this.tree = tree; } /** * Determine if this I/O request has finished. * * @return False if this I/O request has not finished, true if this I/O * request has finished successfully. * * @exception IOException * Thrown if this I/O request has finished and an I/O error * occurred. */ public synchronized boolean isFinished() throws IOException { if (! super.isFinished()) return false; for (IORequest req : myForwardedIORequests) { if (! req.isFinished()) return false; } return true; } /** * Wait until the send or receive operation corresponding to this I/O * request has finished. For a receive operation, a {@linkplain Status} * object containing the results of the receive operation is returned; * for a send operation, null is returned. * * @return Receive status for a receive operation, or null for a send * operation. * * @exception IOException * Thrown if an I/O error occurred. */ public synchronized Status waitForFinish() throws IOException { Status status = super.waitForFinish(); for (IORequest req : myForwardedIORequests) { req.waitForFinish(); } return status; } /** * Report that this I/O request succeeded. */ protected synchronized void reportSuccess() { try { super.reportSuccess(); // Get message tag. int msgtag = myStatus.tag; // Flood the message to every child process in the broadcast // tree. int n = tree.length; for (int i = 1; i < n; ++ i) { IORequest req = new IORequest(); myForwardedIORequests.add (req); myChannelGroup.sendNoWait (getChannel (tree[i]), msgtag, myBuf, req); } } catch (IOException exc) { reportFailure (exc); } } } /** * Broadcast a message to all processes in this communicator. The broadcast * uses a message tag of 0. All processes must call broadcast() * with the same value for root and with a buffer of the same * length and the same item data type. *

* The root process (the process whose rank in this communicator is * root) sends the message items. The message items come from the * given buffer. When the broadcast() method returns, the message * has been fully sent, but it may not yet have been fully received by all * processes. *

* Each non-root process receives the message items. The message items are * stored in the given buffer. When the broadcast() method returns, * the message has been fully received. * * @param root Root process's rank in this communicator. * @param buffer Buffer of data items to be sent (root process) or * received (non-root processes). * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if root is not in the range 0 * .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public void broadcast (int root, Buf buffer) throws IOException { broadcast (root, 0, buffer); } /** * Broadcast a message to all processes in this communicator using the given * message tag. All processes must call broadcast() with the same * values for root and tag and with a buffer of the same * length and the same item data type. *

* The root process (the process whose rank in this communicator is * root) sends the message items. The message items come from the * given buffer. When the broadcast() method returns, the message * has been fully sent, but it may not yet have been fully received by all * processes. *

* Each non-root process receives the message items. The message items are * stored in the given buffer. When the broadcast() method returns, * the message has been fully received. * * @param root Root process's rank in this communicator. * @param tag Message tag. * @param buffer Buffer of data items to be sent (root process) or * received (non-root processes). * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if root is not in the range 0 * .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buffer is null. * @exception IOException * Thrown if an I/O error occurred. */ public void broadcast (int root, int tag, Buf buffer) throws IOException { // Verify preconditions. if (0 > root || root >= mySize) { throw new IndexOutOfBoundsException ("Comm.broadcast(): root = " + root + " out of bounds"); } // Early return if only one process. if (mySize == 1) return; // A broadcast is done as a series of point-to-point messages. The // messages are organized into rounds. The number of rounds is // ceil(log_2(mySize)). In each round, processes send messages to other // processes in parallel. Here is the message pattern for a communicator // with 8 processes doing a broadcast from root process 0: // // Process // 0 1 2 3 4 5 6 7 // | | | | | | | | // |---->| | | | | | | Round 1 // | | | | | | | | // |---------->| | | | | | Round 2 // | |---------->| | | | | // | | | | | | | | // |---------------------->| | | | Round 3 // | |---------------------->| | | // | | |---------------------->| | // | | | |---------------------->| // | | | | | | | | // // If a process other than process 0 is the root, the message pattern is // the same, except the process ranks are circularly rotated. // Get array of process ranks in the broadcast tree. int[] broadcasttree = getBroadcastTree (root); int n = broadcasttree.length; // Receive data from parent if any (blocking). int parent = broadcasttree[0]; if (parent != -1) { myChannelGroup.receive (getChannel (parent), tag, buffer); } // Send data to children if any (non-blocking). IORequest[] iorequest = new IORequest [n]; for (int i = 1; i < n; ++ i) { int child = broadcasttree[i]; iorequest[i] = new IORequest(); myChannelGroup.sendNoWait (getChannel (child), tag, buffer, iorequest[i]); } // Wait for sends to finish if any. for (int i = 1; i < n; ++ i) { iorequest[i].waitForFinish(); } } /** * Scatter messages to all processes in this communicator. The scatter * uses a message tag of 0. All processes must call scatter() * with the same value for root. *

* The root process (the process whose rank in this communicator is * root) sends the message items. The message items sent to process * i come from the source buffer at index i in the given array * of source buffers. When the scatter() method returns, the * messages have been fully sent, but they may not yet have been fully * received by all processes. *

* Each process, including the root process, receives the message items. The * message items are stored in the given destination buffer. This must have * the same length and the same item data type as the corresponding source * buffer. When the scatter() method returns, the message has been * fully received. *

* In the non-root processes, the source buffer array is ignored and may be * null. * * @param root Root process's rank in this communicator. * @param srcarray Array of source buffers to be sent by the root process. * Ignored in the non-root processes. * @param dst Destination buffer to be received. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if root is not in the range 0 * .. size()-1. Thrown if srcarray's length does not * equal the size of this communicator. * @exception NullPointerException * (unchecked exception) Thrown if srcarray or any element * thereof is null. Thrown if dst is null. * @exception IOException * Thrown if an I/O error occurred. */ public void scatter (int root, Buf[] srcarray, Buf dst) throws IOException { scatter (root, 0, srcarray, dst); } /** * Scatter messages to all processes in this communicator using the given * message tag. All processes must call scatter() with the same * values for root and tag. *

* The root process (the process whose rank in this communicator is * root) sends the message items. The message items sent to process * i come from the source buffer at index i in the given array * of source buffers. When the scatter() method returns, the * messages have been fully sent, but they may not yet have been fully * received by all processes. *

* Each process, including the root process, receives the message items. The * message items are stored in the given destination buffer. This must have * the same length and the same item data type as the corresponding source * buffer. When the scatter() method returns, the message has been * fully received. *

* In the non-root processes, the source buffer array is ignored and may be * null. * * @param root Root process's rank in this communicator. * @param tag Message tag. * @param srcarray Array of source buffers to be sent by the root process. * Ignored in the non-root processes. * @param dst Destination buffer to be received. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if root is not in the range 0 * .. size()-1. Thrown if srcarray's length does not * equal the size of this communicator. * @exception NullPointerException * (unchecked exception) Thrown if srcarray or any element * thereof is null. Thrown if dst is null. * @exception IOException * Thrown if an I/O error occurred. */ public void scatter (int root, int tag, Buf[] srcarray, Buf dst) throws IOException { // Verify preconditions. if (0 > root || root >= mySize) { throw new IndexOutOfBoundsException ("Comm.scatter(): root = " + root + " out of bounds"); } // A scatter is done as a series of point-to-point messages. The root // process sends a separate message to every other process. Here is the // message pattern for a communicator with 8 processes scattering from // root process 0: // // Process // 0 1 2 3 4 5 6 7 // | | | | | | | | // |---->| | | | | | | // | | | | | | | | // |---------->| | | | | | // | | | | | | | | // |---------------->| | | | | // | | | | | | | | // |---------------------->| | | | // | | | | | | | | // |---------------------------->| | | // | | | | | | | | // |---------------------------------->| | // | | | | | | | | // |---------------------------------------->| // | | | | | | | | // Root process sends all messages. if (myRank == root) { // Array of IORequest objects for non-blocking sends. IORequest[] iorequest = new IORequest [mySize]; // Initiate sends to lower-ranked processes. for (int rank = 0; rank < myRank; ++ rank) { iorequest[rank] = new IORequest(); myChannelGroup.sendNoWait (getChannel (rank), tag, srcarray[rank], iorequest[rank]); } // Initiate sends to higher-ranked processes. for (int rank = myRank+1; rank < mySize; ++ rank) { iorequest[rank] = new IORequest(); myChannelGroup.sendNoWait (getChannel (rank), tag, srcarray[rank], iorequest[rank]); } // Copy to itself. dst.copy (srcarray[myRank]); // Wait for completion of sends to lower-ranked processes. for (int rank = 0; rank < myRank; ++ rank) { iorequest[rank].waitForFinish(); } // Wait for completion of sends to higher-ranked processes. for (int rank = myRank+1; rank < mySize; ++ rank) { iorequest[rank].waitForFinish(); } } // Non-root process receives one message. else { myChannelGroup.receive (getChannel (root), tag, dst); } } /** * Gather messages from all processes in this communicator. The gather uses * a message tag of 0. All processes must call gather() with the * same value for root. *

* The root process (the process whose rank in this communicator is * root) receives the message items. The message items received * from process i are stored in the destination buffer at index * i in the given array of destination buffers. When the * gather() method returns, all the messages have been fully * received. *

* Each process, including the root process, sends the message items. The * message items come from the given source buffer. This must have the same * length and the same item data type as the corresponding destination * buffer. When the gather() method returns, the message has been * fully sent, but it may not yet have been fully received by the root * process. *

* In the non-root processes, the destination buffer array is ignored and * may be null. * * @param root Root process's rank in this communicator. * @param src Source buffer to be sent. * @param dstarray Array of destination buffers to be received by the root * process. Ignored in the non-root processes. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if root is not in the range 0 * .. size()-1. Thrown if dstarray's length does not * equal the size of this communicator. * @exception NullPointerException * (unchecked exception) Thrown if dstarray or any element * thereof is null. Thrown if src is null. * @exception IOException * Thrown if an I/O error occurred. */ public void gather (int root, Buf src, Buf[] dstarray) throws IOException { gather (root, 0, src, dstarray); } /** * Gather messages from all processes in this communicator using the given * message tag. All processes must call gather() with the same * values for root and tag. *

* The root process (the process whose rank in this communicator is * root) receives the message items. The message items received * from process i are stored in the destination buffer at index * i in the given array of destination buffers. When the * gather() method returns, all the messages have been fully * received. *

* Each process, including the root process, sends the message items. The * message items come from the given source buffer. This must have the same * length and the same item data type as the corresponding destination * buffer. When the gather() method returns, the message has been * fully sent, but it may not yet have been fully received by the root * process. *

* In the non-root processes, the destination buffer array is ignored and * may be null. * * @param root Root process's rank in this communicator. * @param tag Message tag. * @param src Source buffer to be sent. * @param dstarray Array of destination buffers to be received by the root * process. Ignored in the non-root processes. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if root is not in the range 0 * .. size()-1. Thrown if dstarray's length does not * equal the size of this communicator. * @exception NullPointerException * (unchecked exception) Thrown if dstarray or any element * thereof is null. Thrown if src is null. * @exception IOException * Thrown if an I/O error occurred. */ public void gather (int root, int tag, Buf src, Buf[] dstarray) throws IOException { // Verify preconditions. if (0 > root || root >= mySize) { throw new IndexOutOfBoundsException ("Comm.gather(): root = " + root + " out of bounds"); } // A gather is done as a series of point-to-point messages. The root // process receives a separate message from every other process. Here is // the message pattern for a communicator with 8 processes gathering // into root process 0: // // Process // 0 1 2 3 4 5 6 7 // | | | | | | | | // |<----| | | | | | | // | | | | | | | | // |<----------| | | | | | // | | | | | | | | // |<----------------| | | | | // | | | | | | | | // |<----------------------| | | | // | | | | | | | | // |<----------------------------| | | // | | | | | | | | // |<----------------------------------| | // | | | | | | | | // |<----------------------------------------| // | | | | | | | | // Root process receives all messages. if (myRank == root) { // Array of IORequest objects for non-blocking receives. IORequest[] iorequest = new IORequest [mySize]; // Initiate receives from lower-ranked processes. for (int rank = 0; rank < myRank; ++ rank) { iorequest[rank] = new IORequest(); myChannelGroup.receiveNoWait (getChannel (rank), tag, dstarray[rank], iorequest[rank]); } // Initiate receives from higher-ranked processes. for (int rank = myRank+1; rank < mySize; ++ rank) { iorequest[rank] = new IORequest(); myChannelGroup.receiveNoWait (getChannel (rank), tag, dstarray[rank], iorequest[rank]); } // Copy to itself. dstarray[myRank].copy (src); // Wait for completion of receives from lower-ranked processes. for (int rank = 0; rank < myRank; ++ rank) { iorequest[rank].waitForFinish(); } // Wait for completion of receives from higher-ranked processes. for (int rank = myRank+1; rank < mySize; ++ rank) { iorequest[rank].waitForFinish(); } } // Non-root process sends one message. else { myChannelGroup.send (getChannel (root), tag, src); } } /** * All-gather messages from each process to all processes in this * communicator. A message tag of 0 is used. All processes must call * allGather(). *

* Each process sends the message items in the given source buffer. When the * allGather() method returns, the source buffer has been fully * sent. *

* Each process receives message items from the other processes. The message * items received from process i are stored in the destination buffer * at index i in the given array of destination buffers. This * destination buffer must have the same length and the same item data type * as the source buffer in process i. When the allGather() * method returns, all the destination buffers have been fully received. *

* All-gather is the same as gather, except that every process has an array * of destination buffers, and every process receives the results of the * gather. * * @param src Source buffer to be sent. * @param dstarray Array of destination buffers to be received. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if dstarray's length does not * equal the size of this communicator. * @exception NullPointerException * (unchecked exception) Thrown if dstarray or any element * thereof is null. Thrown if src is null. * @exception IOException * Thrown if an I/O error occurred. */ public void allGather (Buf src, Buf[] dstarray) throws IOException { allGather (0, src, dstarray); } /** * All-gather messages from each process to all processes in this * communicator using the given message tag. All processes must call * allGather() with the same value for tag. *

* Each process sends the message items in the given source buffer. When the * allGather() method returns, the source buffer has been fully * sent. *

* Each process receives message items from the other processes. The message * items received from process i are stored in the destination buffer * at index i in the given array of destination buffers. This * destination buffer must have the same length and the same item data type * as the source buffer in process i. When the allGather() * method returns, all the destination buffers have been fully received. *

* All-gather is the same as gather, except that every process has an array * of destination buffers, and every process receives the results of the * gather. * * @param tag Message tag. * @param src Source buffer to be sent. * @param dstarray Array of destination buffers to be received. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if dstarray's length does not * equal the size of this communicator. * @exception NullPointerException * (unchecked exception) Thrown if dstarray or any element * thereof is null. Thrown if src is null. * @exception IOException * Thrown if an I/O error occurred. */ public void allGather (int tag, Buf src, Buf[] dstarray) throws IOException { // Get ranks of predecessor and successor processes. int pred = (myRank - 1 + mySize) % mySize; int succ = (myRank + 1) % mySize; // Copy source buffer into destination buffer at my own rank. dstarray[myRank].copy (src); // Do (mySize-1) message rounds. Messages are sent in a pipelined // fashion from each process to its predecessor until each process's // source data has arrived in every process. Each outgoing message is // overlapped with an incoming message. for (int i = 1; i < mySize; ++ i) { sendReceive (/*toRank */ pred, /*sendTag */ tag, /*sendBuf */ dstarray[(myRank+i-1) % mySize], /*fromRank*/ succ, /*recvTag */ tag, /*recvBuf */ dstarray[(myRank+i) % mySize]); } } /** * Perform a reduction on all processes in this communicator. The reduction * uses a message tag of 0. All processes must call reduce() with * the same value for root, with a buffer of the same length and * the same item data type, and with the same binary operation (class * {@linkplain edu.rit.pj.reduction.Op Op}). *

* Before calling reduce(), each process has a buffer filled with * data items. After reduce() returns, each data item in the root * process's buffer has been set to the reduction of the * corresponding data items in all the processes' buffers. The reduction is * calculated by this formula: *

*     item0 op * item1 op item2 op . . . *

* where op is the binary operation passed in as an argument and * item0, item1, * item2, and so on are the data items in the buffers of * process rank 0, 1, 2, and so on. However, the order in which the data * items actually are combined is not specified. Therefore, the binary * operation must be such that the answer will be the same regardless of the * order in which the data items are combined; that is, the binary operation * must be commutative and associative. *

* In the root process, the reduce operation always changes the buffer's * contents as described above. In the non-root processes, the reduce * operation may or may not change the buffer's contents; the final contents * of the buffer in the non-root processes is not specified. *

* When the reduce() method returns in the root process, the * reduction has been fully performed as described above. When the * reduce() method returns in a non-root process, the non-root * process has sent all its data items into the reduction, but the reduction * may not be fully complete in the root process yet. * * @param root Root process's rank in this communicator. * @param buffer Buffer of data items to be reduced. * @param op Binary operation. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if root is not in the range 0 * .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buf is null or op * is null. * @exception ClassCastException * (unchecked exception) Thrown if buf and op do not * use the same item data type. * @exception IOException * Thrown if an I/O error occurred. */ public void reduce (int root, Buf buffer, Op op) throws IOException { reduce (root, 0, buffer, op); } /** * Perform a reduction on all processes in this communicator using the given * message tag. All processes must call reduce() with the same * value for root, with a buffer of the same length and the same * item data type, and with the same binary operation (class {@linkplain * edu.rit.pj.reduction.Op Op}). *

* Before calling reduce(), each process has a buffer filled with * data items. After reduce() returns, each data item in the root * process's buffer has been set to the reduction of the * corresponding data items in all the processes' buffers. The reduction is * calculated by this formula: *

*     item0 op * item1 op item2 op . . . *

* where op is the binary operation passed in as an argument and * item0, item1, * item2, and so on are the data items in the buffers of * process rank 0, 1, 2, and so on. However, the order in which the data * items actually are combined is not specified. Therefore, the binary * operation must be such that the answer will be the same regardless of the * order in which the data items are combined; that is, the binary operation * must be commutative and associative. *

* In the root process, the reduce operation always changes the buffer's * contents as described above. In the non-root processes, the reduce * operation may or may not change the buffer's contents; the final contents * of the buffer in the non-root processes is not specified. *

* When the reduce() method returns in the root process, the * reduction has been fully performed as described above. When the * reduce() method returns in a non-root process, the non-root * process has sent all its data items into the reduction, but the reduction * may not be fully complete in the root process yet. * * @param root Root process's rank in this communicator. * @param tag Message tag. * @param buffer Buffer of data items to be reduced. * @param op Binary operation. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if root is not in the range 0 * .. size()-1. * @exception NullPointerException * (unchecked exception) Thrown if buf is null or op * is null. * @exception ClassCastException * (unchecked exception) Thrown if buf and op do not * use the same item data type. * @exception IOException * Thrown if an I/O error occurred. */ public void reduce (int root, int tag, Buf buffer, Op op) throws IOException { // Verify preconditions. if (0 > root || root >= mySize) { throw new IndexOutOfBoundsException ("Comm.reduce(): root = " + root + " out of bounds"); } // Early return if only one process. if (mySize == 1) return; // A reduction is done as a series of point-to-point messages. The // messages are organized into rounds. The number of rounds is // ceil(log_2(mySize)). The message pattern is the reverse of the // broadcast message pattern. In each round, processes receive messages // from other processes and reduce the data items into their accumulator // buffers in parallel. When a process has received all messages, it // sends the reduced results on. Here is the message pattern for a // communicator with 8 processes doing a reduction into root process 0: // // Process // 0 1 2 3 4 5 6 7 // | | | | | | | | // |<----------------------| | | | Round 1 // | |<----------------------| | | // | | |<----------------------| | // | | | |<----------------------| // | | | | | | | | // |<----------| | | | | | Round 2 // | |<----------| | | | | // | | | | | | | | // |<----| | | | | | | Round 3 // | | | | | | | | // // If a process other than process 0 is the root, the message pattern is // the same, except the process ranks are circularly rotated. // Get array of process ranks in the broadcast tree. int[] broadcasttree = getBroadcastTree (root); int n = broadcasttree.length; // Set up reduction buffer on top of source buffer. Buf reductionbuf = buffer.getReductionBuf (op); // Receive data from children if any, one at a time in reverse order. for (int i = n-1; i >= 1; -- i) { int child = broadcasttree[i]; myChannelGroup.receive (getChannel (child), tag, reductionbuf); } // Send data to parent if any. int parent = broadcasttree[0]; if (parent != -1) { myChannelGroup.send (getChannel (parent), tag, buffer); } } /** * Perform an all-reduce on all processes in this communicator. The * all-reduce uses a message tag of 0. All processes must call * allReduce() with a buffer of the same length and the same item * data type, and with the same binary operation (class {@linkplain * edu.rit.pj.reduction.Op Op}). *

* Before calling allReduce(), each process has a buffer filled * with data items. After allReduce() returns, each data item in * the calling process's buffer has been set to the reduction of the * corresponding data items in all the processes' buffers. The reduction is * calculated by this formula: *

*     item0 op * item1 op item2 op . . . *

* where op is the binary operation passed in as an argument and * item0, item1, * item2, and so on are the data items in the buffers of * process rank 0, 1, 2, and so on. However, the order in which the data * items actually are combined is not specified. Therefore, the binary * operation must be such that the answer will be the same regardless of the * order in which the data items are combined; that is, the binary operation * must be commutative and associative. *

* The allReduce() method is similar to the reduce() * method, except the results are stored in all the processes' buffers, not * just the one root process's buffer. * * @param buffer Buffer of data items to be reduced. * @param op Binary operation. * * @exception NullPointerException * (unchecked exception) Thrown if buf is null or op * is null. * @exception ClassCastException * (unchecked exception) Thrown if buf and op do not * use the same item data type. * @exception IOException * Thrown if an I/O error occurred. */ public void allReduce (Buf buffer, Op op) throws IOException { allReduce (0, buffer, op); } /** * Perform an all-reduce on all processes in this communicator using the * given message tag. All processes must call allReduce() with a * buffer of the same length and the same item data type, and with the same * binary operation (class {@linkplain edu.rit.pj.reduction.Op Op}). *

* Before calling allReduce(), each process has a buffer filled * with data items. After allReduce() returns, each data item in * the calling process's buffer has been set to the reduction of the * corresponding data items in all the processes' buffers. The reduction is * calculated by this formula: *

*     item0 op * item1 op item2 op . . . *

* where op is the binary operation passed in as an argument and * item0, item1, * item2, and so on are the data items in the buffers of * process rank 0, 1, 2, and so on. However, the order in which the data * items actually are combined is not specified. Therefore, the binary * operation must be such that the answer will be the same regardless of the * order in which the data items are combined; that is, the binary operation * must be commutative and associative. *

* The allReduce() method is similar to the reduce() * method, except the results are stored in all the processes' buffers, not * just the one root process's buffer. * * @param tag Message tag. * @param buffer Buffer of data items to be reduced. * @param op Binary operation. * * @exception NullPointerException * (unchecked exception) Thrown if buf is null or op * is null. * @exception ClassCastException * (unchecked exception) Thrown if buf and op do not * use the same item data type. * @exception IOException * Thrown if an I/O error occurred. */ public void allReduce (int tag, Buf buffer, Op op) throws IOException { // An all-reduce is done using a "butterfly" message passing pattern. // Consider the case of K=8 processes. In the first round, processes one // rank apart exchange data, then each processes accumulates the data // from the other process using the reduction operator. In the second // round, processes two ranks apart exchange and accumulate data. In the // third round, processes four ranks apart exchange and accumulate data. // // Process // 0 1 2 3 4 5 6 7 // | | | | | | | | // |<--->| |<--->| |<--->| |<--->| Round 1 // | | | | | | | | // |<--------->| | |<--------->| | Round 2 // | |<--------->| | |<--------->| // | | | | | | | | // |<--------------------->| | | | Round 3 // | |<--------------------->| | | // | | |<--------------------->| | // | | | |<--------------------->| // | | | | | | | | // // The butterfly pattern works only if the number of processes is a // power of two. If this is not the case, there are two extra message // rounds. Each process outside the butterfly pattern sends its data to // its counterpart inside the butterfly pattern, which accumulates the // data using the reduction operator. Then the butterfly pattern takes // place. Afterwards, each process outside the butterfly pattern // receives the final result from its counterpart inside the butterfly // pattern. For the case of K=10 processes: // // Process // 0 1 2 3 4 5 6 7 8 9 // | | | | | | | | | | // |<----------------------------------------------| | Pre // | |<----------------------------------------------| // | | | | | | | | | | // |<--->| |<--->| |<--->| |<--->| | | Round 1 // | | | | | | | | | | // |<--------->| | |<--------->| | | | Round 2 // | |<--------->| | |<--------->| | | // | | | | | | | | | | // |<--------------------->| | | | | | Round 3 // | |<--------------------->| | | | | // | | |<--------------------->| | | | // | | | |<--------------------->| | | // | | | | | | | | | | // |---------------------------------------------->| | Post // | |---------------------------------------------->| // | | | | | | | | | | // // If K is a power of two, the all-reduce takes (log_2 K) rounds. If K // is not a power of two, the all-reduce takes floor(log_2 K)+2 rounds. // Early exit if only one process. if (mySize == 1) return; // Determine the highest power of 2 less than or equal to this // communicator's size. Processes at this rank and above will be outside // the butterfly message passing pattern. int outside = mySizePowerOf2; // For processes outside the butterfly: if (myRank >= outside) { int insideRank = myRank - outside; // Send initial data to counterpart inside. send (insideRank, tag, buffer); // Receive reduced result from counterpart inside. receive (insideRank, tag, buffer); } // For processes inside the butterfly: else { // Set up temporary receive buffer. Buf receiveBuf = buffer.getTemporaryBuf(); // Set up reduction buffer on top of data buffer. Buf reductionBuf = buffer.getReductionBuf (op); // If there is a counterpart process outside, receive and accumulate // its initial data. int outsideRank = myRank + outside; if (outsideRank < mySize) { receive (outsideRank, tag, reductionBuf); } // Perform butterfly message passing rounds with counterpart // processes inside. int round = 1; while (round < outside) { int otherRank = myRank ^ round; sendReceive (otherRank, tag, buffer, otherRank, tag, receiveBuf); reductionBuf.copy (receiveBuf); round <<= 1; } // If there is a counterpart process outside, send the reduced // result. if (outsideRank < mySize) { send (outsideRank, tag, buffer); } } } /** * Do an all-to-all among all processes in this communicator. A message tag * of 0 is used. *

* srcarray must be an array of K buffers, where K is * the size of this communicator. dstarray must be an array of * K buffers referring to different storage from the source buffers. * For each process rank k, 0 <= k <= K, and each * buffer index i, 0 <= i <= K, the contents of * srcarray[k] in process i are sent to dstarray[i] * in process k. * * @param srcarray Array of source buffers to be sent by this process. * @param dstarray Array of destination buffers to be received by this * process. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if srcarray's length does not * equal the size of this communicator. Thrown if dstarray's * length does not equal the size of this communicator. * @exception NullPointerException * (unchecked exception) Thrown if srcarray or any element * thereof is null. Thrown if dstarray or any element thereof * is null. * @exception IOException * Thrown if an I/O error occurred. */ public void allToAll (Buf[] srcarray, Buf[] dstarray) throws IOException { allToAll (0, srcarray, dstarray); } /** * Do an all-to-all among all processes in this communicator using the given * message tag. All processes must call allToAll() with the same * value for tag. *

* srcarray must be an array of K buffers, where K is * the size of this communicator. dstarray must be an array of * K buffers referring to different storage from the source buffers. * For each process rank k, 0 <= k <= K, and each * buffer index i, 0 <= i <= K, the contents of * srcarray[k] in process i are sent to dstarray[i] * in process k. * * @param tag Message tag. * @param srcarray Array of source buffers to be sent by this process. * @param dstarray Array of destination buffers to be received by this * process. * * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if srcarray's length does not * equal the size of this communicator. Thrown if dstarray's * length does not equal the size of this communicator. * @exception NullPointerException * (unchecked exception) Thrown if srcarray or any element * thereof is null. Thrown if dstarray or any element thereof * is null. * @exception IOException * Thrown if an I/O error occurred. */ public void allToAll (int tag, Buf[] srcarray, Buf[] dstarray) throws IOException { // An all-to-all is done as a series of send-receives. Each process // sends the appropriate buffer to the process one ahead and receives // the appropriate buffer from the process one behind. Then each process // sends the appropriate buffer to the process two ahead and receives // the appropriate buffer from the process two behind. And so on. Here // is the message pattern for a communicator with 4 processes doing an // all-to-all: // // Process // 0 1 2 3 // | | | | // |---->| | | Round 1 // | |---->| | // | | |---->| // - -->| | | |--- - // | | | | // | | | | // |---------->| | Round 2 // | |---------->| // - -->| | |--------- - // - -------->| | |--- - // | | | | // | | | | // |---------------->| Round 3 // - -->| |--------------- - // - -------->| |--------- - // - -------------->| |--- - // | | | | // Copy source to destination at this process's own rank. dstarray[myRank].copy (srcarray[myRank]); // Initiate K-1 non-blocking send-receives. CommRequest[] commrequest = new CommRequest [mySize]; for (int i = 1; i < mySize; ++ i) { int toRank = (myRank + i) % mySize; int fromRank = (myRank - i + mySize) % mySize; commrequest[i] = sendReceive (toRank, tag, srcarray[toRank], fromRank, tag, dstarray[fromRank], (CommRequest) null); } // Wait for completion of all send-receives. for (int i = 1; i < mySize; ++ i) { commrequest[i].waitForFinish(); } } /** * Perform a scan on all processes in this communicator. A message tag of 0 * is used. All processes must call scan() with a buffer of the * same length and the same item data type, and with the same binary * operation (class {@linkplain edu.rit.pj.reduction.Op Op}). *

* Before calling scan(), each process has a buffer filled with * data items. After scan() returns, each data item in the buffer * of process rank i has been set to the reduction of the * corresponding data items in the buffers of process ranks 0 through * i. The reduction is calculated by this formula: *

*     item0 op * item1 op item2 op . . . *

* where op is the binary operation passed in as an argument and * item0, item1, * item2, and so on are the data items in the buffers of * process ranks 0 through i. However, the order in which the data * items actually are combined is not specified. Therefore, the binary * operation must be such that the answer will be the same regardless of the * order in which the data items are combined; that is, the binary operation * must be commutative and associative. * * @param buf Buffer of data items to be scanned. * @param op Binary operation. * * @exception NullPointerException * (unchecked exception) Thrown if buf is null or op * is null. * @exception ClassCastException * (unchecked exception) Thrown if buf and op do not * use the same item data type. * @exception IOException * Thrown if an I/O error occurred. */ public void scan (Buf buf, Op op) throws IOException { scan (0, buf, op); } /** * Perform a scan on all processes in this communicator using the given * message tag. All processes must call scan() with the same value * for tag, with a buffer of the same length and the same item data * type, and with the same binary operation (class {@linkplain * edu.rit.pj.reduction.Op Op}). *

* Before calling scan(), each process has a buffer filled with * data items. After scan() returns, each data item in the buffer * of process rank i has been set to the reduction of the * corresponding data items in the buffers of process ranks 0 through * i. The reduction is calculated by this formula: *

*     item0 op * item1 op item2 op . . . *

* where op is the binary operation passed in as an argument and * item0, item1, * item2, and so on are the data items in the buffers of * process ranks 0 through i. However, the order in which the data * items actually are combined is not specified. Therefore, the binary * operation must be such that the answer will be the same regardless of the * order in which the data items are combined; that is, the binary operation * must be commutative and associative. * * @param tag Message tag. * @param buf Buffer of data items to be scanned. * @param op Binary operation. * * @exception NullPointerException * (unchecked exception) Thrown if buf is null or op * is null. * @exception ClassCastException * (unchecked exception) Thrown if buf and op do not * use the same item data type. * @exception IOException * Thrown if an I/O error occurred. */ public void scan (int tag, Buf buf, Op op) throws IOException { // Early return if only one process. if (mySize == 1) return; // A scan is done as a series of point-to-point messages. The messages // are organized into rounds. The number of rounds is // ceil(log_2(mySize)). In the first round, each process sends its data // to the process one rank ahead, and the incoming data is combined with // the process's data using the reduction operator. In the second round, // each process sends its data to the process two ranks ahead. In the // third round, each process sends its data to process four ranks ahead. // And so on. Here is the message pattern for a communicator with 8 // processes: // // Process // 0 1 2 3 4 5 6 7 // | | | | | | | | // |---->|---->|---->|---->|---->|---->|---->| Round 1 // | | | | | | | | // |---------->| | | | | | Round 2 // | |---------->| | | | | // | | |---------->| | | | // | | | |---------->| | | // | | | | |---------->| | // | | | | | |---------->| // | | | | | | | | // |---------------------->| | | | Round 3 // | |---------------------->| | | // | | |---------------------->| | // | | | |---------------------->| // // Get temporary buffer for holding incoming data items. Buf tempbuf = buf.getTemporaryBuf(); // Get reduction buffer for combining data items. Buf reductionbuf = buf.getReductionBuf (op); // Do rounds of message passing and reduction. int skip = 1; for (;;) { int toRank = myRank + skip; int fromRank = myRank - skip; boolean toExists = 0 <= toRank && toRank < mySize; boolean fromExists = 0 <= fromRank && fromRank < mySize; if (toExists && fromExists) { sendReceive (toRank, tag, buf, fromRank, tag, tempbuf); reductionbuf.copy (tempbuf); } else if (fromExists) { receive (fromRank, tag, reductionbuf); } else if (toExists) { send (toRank, tag, buf); } else break; skip <<= 1; } } /** * Perform an exclusive scan on all processes in this communicator. A * message tag of 0 is used. All processes must call * exclusiveScan() with a buffer of the same length and the same * item data type, with the same binary operation (class {@linkplain * edu.rit.pj.reduction.Op Op}), and with the same initial data value. *

* Before calling exclusiveScan(), each process has a buffer filled * with data items. After exclusiveScan() returns, each data item * in the buffer of process rank i > 0 has been set to the * reduction of the corresponding data items in the buffers of * process ranks 0 through i-1. The reduction is calculated by this * formula: *

*     item0 op * item1 op item2 op . . . *

* where op is the binary operation passed in as an argument and * item0, item1, * item2, and so on are the data items in the buffers of * process ranks 0 through i-1. However, the order in which the data * items actually are combined is not specified. Therefore, the binary * operation must be such that the answer will be the same regardless of the * order in which the data items are combined; that is, the binary operation * must be commutative and associative. *

* In process 0, each data item in the buffer has been set to the initial * data value using the buffer's fill() method. *

* If the buffer's item data type is a primitive type, the item * must be an instance of the corresponding primitive wrapper class -- class * Integer for type int, class Double for type double, and * so on. If the item is null, the item data type's default initial * value is assigned to each element in the buffer. *

* If the buffer's item data type is a nonprimitive type, the item * must be an instance of the item class or a subclass thereof. The * item may be null. Note that since item is * assigned to every buffer element, every buffer element ends up * referring to the same item. * * @param buf Buffer of data items to be scanned. * @param op Binary operation. * @param item Initial data value. * * @exception NullPointerException * (unchecked exception) Thrown if buf is null or op * is null. * @exception ClassCastException * (unchecked exception) Thrown if buf and op do not * use the same item data type. * @exception IOException * Thrown if an I/O error occurred. */ public void exclusiveScan (Buf buf, Op op, Object item) throws IOException { exclusiveScan (0, buf, op, item); } /** * Perform an exclusive scan on all processes in this communicator using the * given message tag. All processes must call exclusiveScan() with * the same value for tag, with a buffer of the same length and the * same item data type, with the same binary operation (class {@linkplain * edu.rit.pj.reduction.Op Op}), and with the same initial data value. *

* Before calling exclusiveScan(), each process has a buffer filled * with data items. After exclusiveScan() returns, each data item * in the buffer of process rank i > 0 has been set to the * reduction of the corresponding data items in the buffers of * process ranks 0 through i-1. The reduction is calculated by this * formula: *

*     item0 op * item1 op item2 op . . . *

* where op is the binary operation passed in as an argument and * item0, item1, * item2, and so on are the data items in the buffers of * process ranks 0 through i-1. However, the order in which the data * items actually are combined is not specified. Therefore, the binary * operation must be such that the answer will be the same regardless of the * order in which the data items are combined; that is, the binary operation * must be commutative and associative. *

* In process 0, each data item in the buffer has been set to the initial * data value using the buffer's fill() method. *

* If the buffer's item data type is a primitive type, the item * must be an instance of the corresponding primitive wrapper class -- class * Integer for type int, class Double for type double, and * so on. If the item is null, the item data type's default initial * value is assigned to each element in the buffer. *

* If the buffer's item data type is a nonprimitive type, the item * must be an instance of the item class or a subclass thereof. The * item may be null. Note that since item is * assigned to every buffer element, every buffer element ends up * referring to the same item. * * @param tag Message tag. * @param buf Buffer of data items to be scanned. * @param op Binary operation. * @param item Initial data value. * * @exception NullPointerException * (unchecked exception) Thrown if buf is null or op * is null. * @exception ClassCastException * (unchecked exception) Thrown if buf and op do not * use the same item data type. * @exception IOException * Thrown if an I/O error occurred. */ public void exclusiveScan (int tag, Buf buf, Op op, Object item) throws IOException { // An exclusive scan begins with each process sending its buffer to the // next higher process. Then process 0 fills its buffer with the initial // data value, while processes 1 and higher do an inclusive scan. int toRank; int fromRank; boolean toExists; boolean fromExists; // Process 0 does this. if (myRank == 0) { // Send buffer to next higher process. toRank = 1; toExists = toRank < mySize; if (toExists) { send (toRank, tag, buf); } // Fill buffer with initial data value. buf.fill (item); } // Processes 1 and higher do this. else { // Get temporary buffer for holding incoming data items. Buf tempbuf = buf.getTemporaryBuf(); // Get reduction buffer for combining data items. Buf reductionbuf = buf.getReductionBuf (op); // Send buffer to next higher process. toRank = myRank + 1; fromRank = myRank - 1; toExists = 0 <= toRank && toRank < mySize; if (toExists) { sendReceive (toRank, tag, buf, fromRank, tag, tempbuf); buf.copy (tempbuf); } else { receive (fromRank, tag, buf); } // Do rounds of message passing and reduction. int skip = 1; for (;;) { toRank = myRank + skip; fromRank = myRank - skip; toExists = 1 <= toRank && toRank < mySize; fromExists = 1 <= fromRank && fromRank < mySize; if (toExists && fromExists) { sendReceive (toRank, tag, buf, fromRank, tag, tempbuf); reductionbuf.copy (tempbuf); } else if (fromExists) { receive (fromRank, tag, reductionbuf); } else if (toExists) { send (toRank, tag, buf); } else break; skip <<= 1; } } } /** * Cause all processes in this communicator to wait at a barrier. The * barrier uses a message tag of 0. All processes must call * barrier(). The calling thread blocks until every process has * called barrier(), then the calling thread unblocks and returns * from the barrier() call. * * @exception IOException * Thrown if an I/O error occurred. */ public void barrier() throws IOException { barrier (0); } /** * Cause all processes in this communicator to wait at a barrier, using the * given message tag. All processes must call barrier() with the * same tag. The calling thread blocks until every process has called * barrier(), then the calling thread unblocks and returns from the * barrier() call. * * @param tag Message tag. * * @exception IOException * Thrown if an I/O error occurred. */ public void barrier (int tag) throws IOException { // A barrier is done as an all-reduce of an empty buffer. allReduce (tag, IntegerBuf.emptyBuffer(), IntegerOp.SUM); } /** * Returns a string version of this communicator. The string includes the * communicator's size, the current process's rank, and the host and port of * each backend process. * * @return String version. */ public String toString() { StringBuilder buf = new StringBuilder(); buf.append ("Comm(size="); buf.append (mySize); buf.append (",rank="); buf.append (myRank); buf.append (",backend"); for (int i = 0; i < mySize; ++ i) { if (i > 0) buf.append (','); buf.append ('['); buf.append (i); buf.append ("]="); buf.append (myAddressForRank[i]); } buf.append (')'); return buf.toString(); } /** * Dump the state of this communicator on the given print stream. For * debugging. * * @param out Print stream. * @param prefix String to print at the beginning of each line. */ public void dump (PrintStream out, String prefix) { out.println(); out.println (prefix+getClass().getName()+"@"+Integer.toHexString(System.identityHashCode(this))); out.println (prefix+"mySize = "+mySize); out.println (prefix+"myRank = "+myRank); out.println (prefix+"myHost = "+myHost); out.println (prefix+"mySizePowerOf2 = "+mySizePowerOf2); out.println (prefix+"myChannelGroup = "+myChannelGroup); out.println (prefix+"myAddressForRank:"); for (int i = 0; i < myAddressForRank.length; ++ i) { out.println (prefix+"\t["+i+"] "+myAddressForRank[i]); } out.println (prefix+"myChannelForRank:"); for (int i = 0; i < myChannelForRank.length; ++ i) { out.println (prefix+"\t["+i+"] "+myChannelForRank[i]); } out.println (prefix+"myBroadcastTree:"); for (int i = 0; i < myBroadcastTree.length; ++ i) { out.print (prefix+"\t["+i+"]"); int[] tree = myBroadcastTree[i]; if (tree == null) { out.print (" null"); } else { for (int j = 0; j < tree.length; ++ j) { out.print (" "+tree[j]); } } out.println(); } out.println(); myChannelGroup.dump (out, prefix); } // Hidden operations. /** * Notify that another process connected a channel to this process. * * @param theChannel Channel. * * @exception IOException * Thrown if an I/O error occurred. */ private synchronized void doFarEndConnected (Channel theChannel) throws IOException { // Record channel and rank. myChannelForRank[getFarRank(theChannel)] = theChannel; // Notify any threads waiting in getChannel(). notifyAll(); } /** * Ensure that a channel for communicating with the process at the given * rank is or will be set up. * * @param farrank Rank of far end process. * * @exception IOException * Thrown if an I/O error occurred. */ private synchronized void ensureChannel (int farrank) throws IOException { // Get channel from channel array. Channel channel = myChannelForRank[farrank]; // If the channel does not exist: if (channel == null) { // If this is the lower-ranked process, set up the connection. if (myRank < farrank) { myChannelForRank[farrank] = myChannelGroup.connect (myAddressForRank[farrank]); } // If this is the higher-ranked process, the lower-ranked process // will set up the connection. } } /** * Get the channel for communicating with the process at the given rank. * * @param farrank Rank of far end process. * * @return Channel. * * @exception IOException * Thrown if an I/O error occurred. */ private synchronized Channel getChannel (int farrank) throws IOException { // Get channel from channel array. Channel channel = myChannelForRank[farrank]; // If the channel does not exist: if (channel == null) { // If this is the lower-ranked process, set up the connection. if (myRank < farrank) { channel = myChannelGroup.connect (myAddressForRank[farrank]); myChannelForRank[farrank] = channel; } // If this is the higher-ranked process, wait for the lower-ranked // process to set up the connection. else { try { while (channel == null) { wait(); channel = myChannelForRank[farrank]; } } catch (InterruptedException exc) { IOException exc2 = new InterruptedIOException(); exc2.initCause (exc); throw exc2; } } } return channel; } /** * Get the rank of the process at the far end of the given channel. * * @param channel Channel. * * @return Far end process rank. */ static int getFarRank (Channel channel) { return channel.farEndChannelGroupId(); } /** * Get an array of process ranks in the broadcast tree for the given root. * The broadcast tree is cached in the field myBroadcastTree for later use. * * @param root Root process's rank. * * @return Broadcast tree. */ private synchronized int[] getBroadcastTree (int root) { if (myBroadcastTree == null) { myBroadcastTree = new int [mySize] []; } int[] broadcasttree = myBroadcastTree[root]; if (broadcasttree == null) { broadcasttree = CommPattern.broadcastPattern (mySize, myRank, root); myBroadcastTree[root] = broadcasttree; } return broadcasttree; } }

Ads help maintain this website.