001/*
002 * $Id: MPIEngine.java 4604 2013-08-25 16:59:02Z kredel $
003 */
004
005package edu.jas.kern;
006
007
008import java.io.IOException;
009import java.util.ArrayList;
010import java.util.Arrays;
011
012import mpi.Comm;
013import mpi.Intracomm;
014import mpi.MPI;
015import mpi.MPIException;
016import mpi.Status;
017
018import org.apache.log4j.Logger;
019
020
021/**
022 * MPI engine, provides global MPI service. <b>Note:</b> could eventually be
023 * done directly with MPI, but provides logging. <b>Usage:</b> To obtain a
024 * reference to the MPI service communicator use
025 * <code>MPIEngine.getComminicator()</code>. Once an engine has been created it
026 * must be shutdown to exit JAS with <code>MPIEngine.terminate()</code>.
027 * @author Heinz Kredel
028 */
029
030public final class MPIEngine {
031
032
033    private static final Logger logger = Logger.getLogger(MPIEngine.class);
034
035
036    private static final boolean debug = logger.isDebugEnabled();
037
038
039    /**
040     * Command line arguments. Required for MPI runtime system.
041     */
042    protected static String[] cmdline;
043
044
045    /**
046     * Hostnames of MPI partners.
047     */
048    public static ArrayList<String> hostNames = new ArrayList<String>();
049
050
051    /**
052     * Flag for MPI usage. <b>Note:</b> Only introduced because Google app
053     * engine does not support MPI.
054     */
055    public static boolean NO_MPI = false;
056
057
058    /**
059     * Number of processors.
060     */
061    public static final int N_CPUS = Runtime.getRuntime().availableProcessors();
062
063
064    /*
065     * Core number of threads.
066     * N_CPUS x 1.5, x 2, x 2.5, min 3, ?.
067     */
068    public static final int N_THREADS = (N_CPUS < 3 ? 3 : N_CPUS + N_CPUS / 2);
069
070
071    /**
072     * MPI communicator engine.
073     */
074    static Intracomm mpiComm;
075
076
077    /**
078     * MPI engine base tag number.
079     */
080    public static final int TAG = 11;
081
082
083    /**
084     * Hostname suffix.
085     */
086    public static final String hostSuf = "-ib";
087
088
089    // /*
090    //  * Send locks per tag.
091    //  */
092    // private static SortedMap<Integer,Object> sendLocks = new TreeMap<Integer,Object>();
093
094
095    // /*
096    //  * receive locks per tag.
097    //  */
098    // private static SortedMap<Integer,Object> recvLocks = new TreeMap<Integer,Object>();
099
100
101    /**
102     * No public constructor.
103     */
104    private MPIEngine() {
105    }
106
107
108    /**
109     * Set the commandline.
110     * @param args the command line to use for the MPI runtime system.
111     */
112    public static synchronized void setCommandLine(String[] args) {
113        cmdline = args;
114    }
115
116
117    /**
118     * Test if a pool is running.
119     * @return true if a thread pool has been started or is running, else false.
120     */
121    public static synchronized boolean isRunning() {
122        if (mpiComm == null) {
123            return false;
124        }
125        return true;
126    }
127
128
129    /**
130     * Get the MPI communicator.
131     * @return a Communicator constructed for cmdline.
132     */
133    public static synchronized Comm getCommunicator() throws IOException, MPIException {
134        if (cmdline == null) {
135            throw new IllegalArgumentException("command line not set");
136        }
137        return getCommunicator(cmdline);
138    }
139
140
141    /**
142     * Get the MPI communicator.
143     * @param args the command line to use for the MPI runtime system.
144     * @return a Communicator.
145     */
146    public static synchronized Comm getCommunicator(String[] args) throws IOException, MPIException {
147        if (NO_MPI) {
148            return null;
149        }
150        if (mpiComm == null) {
151            //String[] args = new String[] { }; //"-np " + N_THREADS };
152            if (args == null) {
153                throw new IllegalArgumentException("command line is null");
154            }
155            cmdline = args;
156            args = MPI.Init(args);
157            //int tl = MPI.Init_thread(args,MPI.THREAD_MULTIPLE);
158            logger.info("MPI initialized on " + MPI.Get_processor_name());
159            //logger.info("thread level MPI.THREAD_MULTIPLE: " + MPI.THREAD_MULTIPLE 
160            //            + ", provided: " + tl);
161            if (debug) {
162                logger.debug("remaining args: " + Arrays.toString(args));
163            }
164            mpiComm = MPI.COMM_WORLD;
165            int size = mpiComm.Size();
166            int rank = mpiComm.Rank();
167            logger.info("MPI size = " + size + ", rank = " + rank);
168            // maintain list of hostnames of partners
169            hostNames.ensureCapacity(size);
170            for (int i = 0; i < size; i++) {
171                hostNames.add("");
172            }
173            String myhost = MPI.Get_processor_name();
174            if ( myhost.matches("\\An\\d*") ) { // bwGRiD node names n010207
175                myhost += hostSuf;
176            }
177            if ( myhost.matches("kredel.*") ) { 
178                myhost = "localhost";
179            }
180            hostNames.set(rank, myhost);
181            if (rank == 0) {
182                String[] va = new String[1];
183                va[0] = hostNames.get(0);
184                mpiComm.Bcast(va, 0, va.length, MPI.OBJECT, 0);
185                for (int i = 1; i < size; i++) {
186                    Status stat = mpiComm.Recv(va, 0, va.length, MPI.OBJECT, i, TAG);
187                    if (stat == null) {
188                        throw new IOException("no Status received");
189                        //throw new MPIException("no Status received");
190                    }
191                    int cnt = stat.Get_count(MPI.OBJECT);
192                    if (cnt == 0) {
193                        throw new IOException("no Object received");
194                        //throw new MPIException("no object received");
195                    }
196                    String v = va[0];
197                    hostNames.set(i, v);
198                }
199                logger.info("MPI partner host names = " + hostNames);
200            } else {
201                String[] va = new String[1];
202                mpiComm.Bcast(va, 0, va.length, MPI.OBJECT, 0);
203                hostNames.set(0, va[0]);
204                va[0] = hostNames.get(rank);
205                mpiComm.Send(va, 0, va.length, MPI.OBJECT, 0, TAG);
206            }
207        }
208        return mpiComm;
209    }
210
211
212    /**
213     * Stop execution.
214     */
215    public static synchronized void terminate() {
216        if (mpiComm == null) {
217            return;
218        }
219        try {
220            logger.info("terminating MPI on rank = " + mpiComm.Rank());
221            mpiComm = null;
222            MPI.Finalize();
223        } catch (MPIException e) {
224            e.printStackTrace();
225        }
226    }
227
228
229    /**
230     * Set no MPI usage.
231     */
232    public static synchronized void setNoMPI() {
233        NO_MPI = true;
234        terminate();
235    }
236
237
238    /**
239     * Set MPI usage.
240     */
241    public static synchronized void setMPI() {
242        NO_MPI = false;
243    }
244
245
246    // /*
247    //  * Get send lock per tag.
248    //  * @param tag message tag.
249    //  * @return a lock for sends.
250    //  */
251    // public static synchronized Object getSendLock(int tag) {
252    //     tag = 11; // one global lock
253    //     Object lock = sendLocks.get(tag);
254    //     if ( lock == null ) {
255    //         lock = new Object();
256    //         sendLocks.put(tag,lock);
257    //     }
258    //     return lock;
259    // }
260
261
262    // /*
263    //  * Get receive lock per tag.
264    //  * @param tag message tag.
265    //  * @return a lock for receives.
266    //  */
267    // public static synchronized Object getRecvLock(int tag) {
268    //     Object lock = recvLocks.get(tag);
269    //     if ( lock == null ) {
270    //         lock = new Object();
271    //         recvLocks.put(tag,lock);
272    //     }
273    //     return lock;
274    // }
275
276
277    // /*
278    //  * Wait for termination of a mpi Request.
279    //  * @param req a Request.
280    //  * @return a Status after termination of req.Wait().
281    //  */
282    // public static Status waitRequest(final Request req) throws MPIException {
283    //     if ( req == null || req.Is_null() ) {
284    //         throw new IllegalArgumentException("null request");
285    //     }
286    //     int delay = 50;
287    //     int delcnt = 0;
288    //     Status stat = null;
289    //     while (true) {
290    //         synchronized (MPIEngine.class) { // global static lock
291    //             stat = req.Test(); 
292    //             logger.info("Request: " + req + ", Status: " + stat);
293    //             if (stat != null) {
294    //                 logger.info("Status: index = " + stat.index + ", source = " + stat.source
295    //                                   + ", tag = " + stat.tag);
296    //                 if (!stat.Test_cancelled()) {
297    //                     logger.info("enter req.Wait(): " + Thread.currentThread().toString());
298    //                     return req.Wait(); // should terminate immediately
299    //                 }
300    //             }
301    //         }
302    //         try {
303    //             Thread.currentThread().sleep(delay); // varied a bit
304    //         } catch (InterruptedException e) {
305    //             logger.info("sleep interrupted");
306    //             e.printStackTrace();
307    //         }
308    //         delcnt++; 
309    //         if ( delcnt % 7 != 0 ) {
310    //             delay++;
311    //             logger.info("delay(" + delay + "): " + Thread.currentThread().toString());
312    //         } 
313    //     }
314    // }
315
316}