Line data Source code
1 : //# Applicator.cc: Implementation of Applicator.h
2 : //# Copyright (C) 1999,2000,2002
3 : //# Associated Universities, Inc. Washington DC, USA.
4 : //#
5 : //# This library is free software; you can redistribute it and/or modify it
6 : //# under the terms of the GNU Library General Public License as published by
7 : //# the Free Software Foundation; either version 2 of the License, or (at your
8 : //# option) any later version.
9 : //#
10 : //# This library is distributed in the hope that it will be useful, but WITHOUT
11 : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 : //# License for more details.
14 : //#
15 : //# You should have received a copy of the GNU Library General Public License
16 : //# along with this library; if not, write to the Free Software Foundation,
17 : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
18 : //#
19 : //# Correspondence concerning AIPS++ should be addressed as follows:
20 : //# Internet email: casa-feedback@nrao.edu.
21 : //# Postal address: AIPS++ Project Office
22 : //# National Radio Astronomy Observatory
23 : //# 520 Edgemont Road
24 : //# Charlottesville, VA 22903-2475 USA
25 : //#
26 : //# $Id$
27 :
28 : #include <casacore/casa/Utilities/Assert.h>
29 :
30 : #include <synthesis/Parallel/Applicator.h>
31 : #include <synthesis/Parallel/MPITransport.h>
32 : #include <synthesis/Parallel/SerialTransport.h>
33 : #include <synthesis/Parallel/Algorithm.h>
34 : #include <synthesis/MeasurementComponents/ClarkCleanAlgorithm.h>
35 : #include <synthesis/MeasurementComponents/ReadMSAlgorithm.h>
36 : #include <synthesis/MeasurementComponents/MakeApproxPSFAlgorithm.h>
37 : #include <synthesis/MeasurementComponents/PredictAlgorithm.h>
38 : #include <synthesis/MeasurementComponents/ResidualAlgorithm.h>
39 : #include <synthesis/ImagerObjects/CubeMajorCycleAlgorithm.h>
40 : #include <synthesis/ImagerObjects/CubeMakeImageAlgorithm.h>
41 : #include <synthesis/ImagerObjects/CubeMinorCycleAlgorithm.h>
42 : #include <synthesis/Parallel/MPIError.h>
43 :
44 : using namespace casacore;
45 : using namespace std;
46 : namespace casa { //# NAMESPACE CASA - BEGIN
47 :
48 5 : Applicator::Applicator() : comm(0), algorithmIds( ),
49 5 : knownAlgorithms( ), LastID(101), usedAllThreads(false),
50 5 : serial(true), nProcs(0), procStatus(0), initialized_p(false)
51 : {
52 : // Default constructor; requires later init().
53 5 : }
54 :
55 5 : Applicator::~Applicator()
56 : {
57 : // Default destructor
58 : //
59 5 : if (comm) {
60 : // If controller, then stop all worker processes
61 0 : if (isController() && !(comm->isFinalized())) {
62 0 : comm->setTag(STOP);
63 0 : for (Int i=0; i<nProcs; i++) {
64 0 : if (i != comm->controllerRank()) {
65 0 : comm->connect(i);
66 0 : put(STOP);
67 : }
68 : }
69 : }
70 0 : delete comm;
71 : }
72 :
73 5 : for (auto &algo : knownAlgorithms) {
74 0 : delete algo.second;
75 : }
76 5 : }
77 :
78 0 : void Applicator::initThreads(Int argc, Char *argv[]){
79 :
80 0 : Int numprocs=0;
81 :
82 : // A no-op if not using MPI
83 : #ifdef HAVE_MPI
84 : //if (debug_p) {
85 :
86 0 : if(initialized_p) return;
87 :
88 : //If detecting only 1 proc is offered to OpenMPI but compiling with MPI
89 0 : if (!getenv("OMPI_COMM_WORLD_LOCAL_SIZE") || (String::toInt(getenv("OMPI_COMM_WORLD_LOCAL_SIZE")) <2) ) {
90 : //go serial
91 0 : initThreads();
92 : }
93 : else {
94 : //cerr << "In initThreads. argc: " << argc << ", argv: " << argv << '\n';
95 0 : int flag=0;
96 0 : MPI_Initialized(&flag);
97 : //cerr << "FLAG " << flag << endl;
98 0 : if(flag || MPI_Init(&argc, &argv)==MPI_SUCCESS){
99 0 : Int numproc=0;
100 0 : MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
101 0 : if(numprocs < 2){
102 0 : initThreads();
103 0 : MPI_Finalize();
104 0 : return;
105 : }
106 : }
107 :
108 : // cerr << "In initThreads. argc: " << argc << ", argv: " << argv << '\n';
109 : // Initialize the MPI transport layer
110 : try {
111 0 : comm = new MPITransport(argc, argv);
112 :
113 : // Initialize the process status list
114 0 : setupProcStatus();
115 :
116 : // If controller then exit, else loop, waiting for an assigned task
117 0 : if (isWorker()) {
118 0 : loop();
119 : }
120 :
121 0 : } catch (MPIError x) {
122 0 : cerr << x.getMesg() << " doing serial "<< endl;
123 0 : initThreads();
124 0 : }
125 : }
126 :
127 : #else
128 : (void)argc;
129 : (void)argv;
130 : cerr << " doing serial "<< endl;
131 : initThreads();
132 : #endif
133 : }
134 :
135 : // Serial transport all around.
136 0 : void Applicator::initThreads(){
137 : // Initialize a serial transport layer
138 0 : comm = new SerialTransport();
139 : // Initialize the process status list
140 0 : setupProcStatus();
141 0 : }
142 0 : void Applicator::destroyThreads(){
143 0 : if(initialized_p){
144 0 : if (comm) {
145 : // If controller, then stop all worker processes
146 0 : if (isController() && !isSerial() && !(comm->isFinalized())) {
147 : //comm->setTag(STOP);
148 0 : for (Int i=0; i<nProcs; i++) {
149 0 : if (i != comm->controllerRank()) {
150 0 : comm->connect(i);
151 0 : comm->setTag(STOP);
152 0 : put(STOP);
153 :
154 : }
155 : }
156 : }
157 : //delete comm; ///leaking this for now as if initialized from python..it brings down the whole house
158 : //comm=nullptr;
159 : }
160 :
161 : }
162 :
163 0 : }
164 0 : void Applicator::init(Int argc, Char *argv[])
165 : {
166 : // Initialize the process and parallel transport layer
167 : //
168 : //cerr <<"Applicatorinit " << initialized_p << endl;
169 0 : if(comm){
170 : //if worker was released from loop...want it back now
171 0 : if(comm && isWorker() && !isSerial())
172 0 : loop();
173 0 : return;
174 : }
175 : // Fill the map of known algorithms
176 : //cerr << "APPINIT defining algorithms " << endl;
177 0 : defineAlgorithms();
178 :
179 : #ifdef HAVE_MPI
180 0 : if (debug_p) {
181 0 : cerr << "In init threads, HAVE_MPI...\n";
182 : }
183 0 : initThreads(argc, argv);
184 : #else
185 : if (debug_p) {
186 : cerr << "In init threads, not HAVE_MPI...\n";
187 : }
188 : (void)argc;
189 : (void)argv;
190 : initThreads();
191 : #endif
192 0 : initialized_p=true;
193 0 : return;
194 : }
195 :
196 0 : Bool Applicator::isController()
197 : {
198 : // Return T if the current process is the controller
199 : //
200 : Bool result;
201 0 : if (comm) {
202 0 : result = comm->isController();
203 : } else {
204 0 : throw(AipsError("Parallel transport layer not initialized"));
205 : }
206 0 : return result;
207 : }
208 :
209 0 : Bool Applicator::isWorker()
210 : {
211 : // Return T if the current process is a worker process
212 : //
213 : Bool result;
214 0 : if (comm) {
215 0 : result = comm->isWorker();
216 : } else {
217 0 : throw(AipsError("Parallel transport layer not initialized"));
218 : }
219 0 : return result;
220 : }
221 :
222 0 : void Applicator::loop()
223 : {
224 : // Loop, if a worker process, waiting for an assigned task
225 : //
226 0 : Bool die(false);
227 : Int what;
228 : // Wait for a message from the controller with any Algorithm tag
229 0 : while(!die){
230 0 : comm->connectToController();
231 0 : comm->setAnyTag();
232 : //cerr << "in loop get" << endl;
233 0 : comm->get(what);
234 0 : if (debug_p) {
235 0 : cerr << "worker, got what (algID/stop): " << what << endl;
236 : }
237 0 : switch(what){
238 0 : case STOP :
239 0 : die = true;
240 0 : break;
241 0 : default :
242 : // In this case, an Algorithm tag is expected.
243 : // First check that it is known.
244 0 : if (knownAlgorithms.find(what) != knownAlgorithms.end( )) {
245 : // Identified algorithm tag; set for subsequent communication
246 0 : comm->setTag(what);
247 : // Execute (apply) the algorithm
248 0 : knownAlgorithms.at(what)->apply();
249 : } else {
250 0 : throw(AipsError("Unidentified parallel algorithm code"));
251 : }
252 0 : break;
253 : }
254 : }
255 : //cerr <<"getting out of loop " <<endl;
256 0 : return;
257 : }
258 :
259 0 : Bool Applicator::nextAvailProcess(Algorithm &a, Int &rank)
260 : {
261 : // Assign the next available process for the specified Algorithm
262 : //
263 : // Must be the controller to request a worker process
264 0 : Bool assigned=False;
265 0 : if (isWorker()) {
266 0 : throw(AipsError("Must be the controller to assign a worker process"));
267 : } else {
268 0 : if (!usedAllThreads) {
269 : // Connect to the next available process in the list
270 : Bool lastOne;
271 0 : rank = findFreeProc(lastOne);
272 0 : AlwaysAssert(rank >= 0, AipsError);
273 0 : if (lastOne) usedAllThreads = true;
274 0 : Int tag = algorithmIds.find(a.name()) == algorithmIds.end( ) ? 0 : algorithmIds.at(a.name());
275 :
276 : // Send wake-up message (containing the Algorithm tag) to
277 : // the assigned worker process to activate it (see loop()).
278 0 : comm->connect(rank);
279 0 : comm->setTag(tag);
280 : //cerr << "nextAvailproc settag " << tag << " rank " << rank << " name " << a.name() << endl;
281 0 : put(tag);
282 : /*
283 : if (not isWorker() and numProcs() <= 1){
284 : // the first int, algID, is consumed in the loop for the workers when running
285 : // in multiprocess mode and there are at least 2 processes. When not multiprocess or a
286 : // single process, we need to consume it:
287 : // TODO - it could be consumed up here, right after the put()
288 : int algID;
289 : comm->get(algID);
290 : if (debug_p) {
291 : cerr << "nextAvailproc controller, got algID: " << algID << " assigned " << assigned << " donesig " << donesig_p<< endl;
292 : }
293 : }
294 : */
295 0 : assigned = true;
296 0 : procStatus(rank) = ASSIGNED;
297 : } else {
298 0 : assigned = false;
299 : }
300 : }
301 : //cerr << "nextAvailproc controller assigned " << assigned << endl;
302 :
303 0 : if ((!isWorker()) && (numProcs() <= 1) && assigned){
304 : // the first int, algID, is consumed in the loop for the workers when running
305 : // in multiprocess mode and there are at least 2 processes. When not multiprocess or a
306 : // single process, we need to consume it:
307 : // TODO - it could be consumed up here, right after the put()
308 : Int algID;
309 : //comm->get(algID);
310 0 : get(algID);
311 0 : if (debug_p) {
312 0 : cerr << "nextAvailproc controller, got algID: " << algID << " assigned " << assigned << " donesig " << donesig_p<< endl;
313 : }
314 : }
315 :
316 0 : return assigned;
317 : }
318 :
319 0 : bool Applicator::initialized(){
320 : #ifdef HAVE_MPI
321 0 : return initialized_p;
322 : #endif
323 :
324 : return false;
325 : }
326 0 : Int Applicator::nextProcessDone(Algorithm &a, Bool &allDone)
327 : {
328 : // Return the rank of the next process to complete the specified algorithm
329 : //
330 0 : Int rank = -1;
331 0 : allDone = true;
332 : //cerr << "nextprocess done procstatus " << procStatus << endl;
333 0 : for (uInt i=0; i<procStatus.nelements(); i++) {
334 0 : if (procStatus(i) == ASSIGNED) {
335 0 : if (isSerial()) {
336 : // In the serial case, the controller can be assigned
337 0 : allDone = false;
338 : } else {
339 : // In the parallel case, the controller is not assigned
340 0 : if (i != static_cast<uInt>(comm->controllerRank())) {
341 0 : allDone = false;
342 : }
343 : }
344 : }
345 : }
346 0 : if (!allDone) {
347 : // Wait for a process to finish with the correct algorithm tag
348 0 : comm->connectAnySource();
349 0 : Int tag = algorithmIds.find(a.name()) == algorithmIds.end( ) ? 0 : algorithmIds.at(a.name());
350 : //cerr <<"procdone name" << a.name() << " id " << tag << endl;
351 0 : comm->setTag(tag);
352 : Int doneSignal;
353 0 : rank = get(doneSignal);
354 : //cerr <<" procdone rank " << rank << " donesig " << doneSignal << endl;
355 : // Consistency check; should return a DONE signal to contoller
356 : // on completion.
357 0 : if (doneSignal != DONE) {
358 0 : throw(AipsError("Worker process terminated unexpectedly"));
359 : } else {
360 : // Set source in parallel transport layer
361 0 : comm->connect(rank);
362 : // Mark process as free
363 0 : procStatus(rank) = FREE;
364 : //cerr << "NEXTProcDone connect rank" << rank << " procstat " << procStatus << endl;
365 0 : usedAllThreads = false;
366 : }
367 : }
368 0 : return rank;
369 : }
370 :
371 0 : void Applicator::done()
372 : {
373 : // Signal that a worker process is done
374 : //
375 0 : donesig_p=DONE;
376 0 : Int donesig=DONE;
377 0 : if(isSerial())
378 0 : put(donesig_p);
379 : else
380 0 : put(donesig);
381 0 : return;
382 : }
383 :
384 0 : void Applicator::apply(Algorithm &a)
385 : {
386 : // Execute an algorithm directly
387 : //
388 : // Null operation unless serial, in which case the
389 : // controller needs to execute the algorithm directly.
390 : // In the parallel case, the algorithm applies are
391 : // performed in workers processes' applicator.init().
392 0 : donesig_p=10000;
393 0 : if (isSerial() && isController()) {
394 0 : a.apply();
395 : }
396 0 : return;
397 : }
398 :
399 0 : void Applicator::defineAlgorithm(Algorithm *a)
400 : {
401 : //no need to add if it is already defined
402 : // if(algorithmIds.count(a->name()) <1){
403 : //knownAlgorithms.insert( std::pair<casacore::Int,Algorithm*>(LastID, a) );
404 : // algorithmIds.insert( std::pair<casacore::String, casacore::Int>(a->name(), LastID) );
405 0 : Int theid=LastID;
406 0 : if(algorithmIds.count(a->name()) >0){
407 0 : theid=algorithmIds[a->name()];
408 : }
409 : else{
410 0 : theid=LastID;
411 0 : algorithmIds[a->name()]=LastID;
412 0 : ++LastID;
413 : }
414 0 : knownAlgorithms[theid]=a;
415 : // }
416 0 : return;
417 : }
418 :
419 0 : void Applicator::defineAlgorithms()
420 : {
421 : // Fill the algorithm map
422 : //
423 : // Clark CLEAN parallel deconvolution
424 0 : Algorithm *a1 = new ClarkCleanAlgorithm;
425 0 : knownAlgorithms.insert( std::pair<casacore::Int, Algorithm*>(LastID, a1) );
426 0 : algorithmIds.insert( std::pair<casacore::String, casacore::Int>(a1->name(), LastID) );
427 0 : LastID++;
428 0 : Algorithm *a2 = new ReadMSAlgorithm;
429 0 : knownAlgorithms.insert( std::pair<casacore::Int, Algorithm*>(LastID, a2) );
430 0 : algorithmIds.insert( std::pair<casacore::String, casacore::Int>(a2->name(), LastID) );
431 0 : LastID++;
432 0 : Algorithm *a3 = new MakeApproxPSFAlgorithm;
433 0 : knownAlgorithms.insert( std::pair<casacore::Int, Algorithm*>(LastID, a3) );
434 0 : algorithmIds.insert( std::pair<casacore::String, casacore::Int>(a3->name(), LastID) );
435 0 : LastID++;
436 0 : Algorithm *a4 = new PredictAlgorithm;
437 0 : knownAlgorithms.insert( std::pair<casacore::Int, Algorithm*>(LastID, a4) );
438 0 : algorithmIds.insert( std::pair<casacore::String, casacore::Int>(a4->name(), LastID) );
439 0 : LastID++;
440 0 : Algorithm *a5 = new ResidualAlgorithm;
441 0 : knownAlgorithms.insert( std::pair<casacore::Int, Algorithm*>(LastID, a5) );
442 0 : algorithmIds.insert( std::pair<casacore::String, casacore::Int>(a5->name(), LastID) );
443 0 : LastID++;
444 0 : Algorithm *a6 = new CubeMajorCycleAlgorithm;
445 0 : knownAlgorithms.insert( std::pair<casacore::Int, Algorithm*>(LastID, a6) );
446 0 : algorithmIds.insert( std::pair<casacore::String, casacore::Int>(a6->name(), LastID) );
447 0 : LastID++;
448 0 : Algorithm *a7 = new CubeMakeImageAlgorithm;
449 0 : knownAlgorithms.insert( std::pair<casacore::Int, Algorithm*>(LastID, a7) );
450 0 : algorithmIds.insert( std::pair<casacore::String, casacore::Int>(a7->name(), LastID) );
451 0 : LastID++;
452 0 : Algorithm *a8 = new CubeMinorCycleAlgorithm;
453 0 : knownAlgorithms.insert( std::pair<casacore::Int, Algorithm*>(LastID, a8) );
454 0 : algorithmIds.insert( std::pair<casacore::String, casacore::Int>(a8->name(), LastID) );
455 0 : LastID++;
456 0 : return;
457 : }
458 :
459 0 : void Applicator::setupProcStatus()
460 : {
461 : // Set up the process status list
462 : //
463 0 : nProcs = comm->numThreads();
464 0 : if (nProcs <= 1) {
465 0 : serial = true;
466 : } else {
467 0 : serial = false;
468 : }
469 : // Resize the process list, and mark as unassigned (except for controller)
470 0 : usedAllThreads = false;
471 0 : procStatus.resize(max(nProcs,1));
472 0 : procStatus = FREE;
473 : // In the parallel case, the controller is never assigned
474 0 : if (!isSerial())
475 0 : procStatus(comm->controllerRank()) = ASSIGNED;
476 0 : }
477 :
478 0 : Int Applicator::findFreeProc(Bool &lastOne)
479 : {
480 : // Search the process status list for the next free process
481 : //
482 0 : Int freeProc = -1;
483 0 : Int nfree = 0;
484 :
485 0 : for (uInt i=0; i<procStatus.nelements(); i++) {
486 0 : if (procStatus(i) == FREE) {
487 0 : nfree++;
488 0 : if (freeProc < 0) freeProc = i;
489 : }
490 : }
491 0 : lastOne = (nfree==1);
492 : //cerr <<"FreeProc procstat "<< procStatus << " nfree " << nfree << endl;
493 0 : return freeProc;
494 : }
495 :
496 : // The applicator is ominpresent.
497 : // Moved here for shared libraries.
498 : Applicator applicator;
499 :
500 :
501 : } //# NAMESPACE CASA - END
502 :
|