13.7.8. Macro “relauncherCodeFlowrateMpiStandalone.C”
13.7.8.1. Objective
The goal of this macro is to show how to handle a code run on several threads with another memory
paradigm: when the TThreadedRun instance is relying on shared memory (leading to possible
thread-safe problem, as discussed in TThreadedRun), the MPI implementation is
based on the separation of the memory. The communication is made through messages. In order to this,
the usual sequential runner will be removed and another runner will be called to do the job. The
flowrate code is provided with Uranie and has been also used and discussed throughout these
macros.
Warning
This macro is different from the one discussed previously in Macro “relauncherCodeFlowrateMPI.C” as here, one wants to handle MPI through standalone compilation (so C++ without using the ROOT interface)
13.7.8.2. Macro
#include "TAttribute.h"
#include "TCodeEval.h"
#include "TFlatScript.h"
#include "TDataServer.h"
#include "TFlatResult.h"
#include "TMpiRun.h"
#include "TLauncher2.h"
void usage(const char *scmd)
{
printf("\n");
printf("Usage: %s initType Type \n", scmd);
printf("\n");
printf(" Specific method to test standalone MPI distribution with reoptimizer dummy case.\n");
printf("\t initType : The MPI initialisation chosen (explicit <-> 0 or implicit <-> 1) \n");
printf("\n");
}
int main(int argc, char **argv)
{
int initType=0; // explicit <-> 0 or implicit <-> 1
if(argc>1)
{
for (int iarg=1; iarg<argc; iarg++)
{
if( string(argv[iarg]) == "initType") { initType=atof(argv[iarg+1]); iarg++;}
else
{
cout<<"==============================================================="<<endl;
cout<<"Don't know the following option ? "<<string(argv[iarg])<<endl;
usage("mpirun -np N ./${EXECNAME}");
cout<<"==============================================================="<<endl;
return 0;
}
}
}
// variables
URANIE::DataServer::TAttribute *rw = new URANIE::DataServer::TAttribute("rw");
URANIE::DataServer::TAttribute *r = new URANIE::DataServer::TAttribute("r");
URANIE::DataServer::TAttribute *tu = new URANIE::DataServer::TAttribute("tu");
URANIE::DataServer::TAttribute *tl = new URANIE::DataServer::TAttribute("tl");
URANIE::DataServer::TAttribute *hu = new URANIE::DataServer::TAttribute("hu");
URANIE::DataServer::TAttribute *hl = new URANIE::DataServer::TAttribute("hl");
URANIE::DataServer::TAttribute *l = new URANIE::DataServer::TAttribute("l");
URANIE::DataServer::TAttribute *kw = new URANIE::DataServer::TAttribute("kw");
// Create the output attribute
URANIE::DataServer::TAttribute *yhat = new URANIE::DataServer::TAttribute("yhat");
URANIE::DataServer::TAttribute *d = new URANIE::DataServer::TAttribute("d");
// Set the reference input file and the key for each input attributes
URANIE::Relauncher::TFlatScript fin("flowrate_input_with_values_rows.in");
fin.setInputs(8, rw, r, tu, tl, hu, hl, l, kw);
// The output file of the code
URANIE::Relauncher::TFlatResult fout("_output_flowrate_withRow_.dat");
fout.setOutputs(2, yhat, d);
// Instanciation de mon code
URANIE::Relauncher::TCodeEval mycode("flowrate -s -r");
//mycode.setOldTmpDir();
mycode.addInputFile(&fin);
mycode.addOutputFile(&fout);
// Create a runner
URANIE::MpiRelauncher::TMpiRun *run=NULL;
if(initType==0)
{
MPI_Init(&argc, &argv);
run = new URANIE::MpiRelauncher::TMpiRun(&mycode);
}
else if(initType==1)
run = new URANIE::MpiRelauncher::TMpiRun(&mycode, &argc, &argv);
run->startSlave();
if(run->onMaster())
{
// Create the TDS
URANIE::DataServer::TDataServer tds("launchFlowrate", "launching flowrate with mpi");
mycode.addAllInputs(&tds);
tds.fileDataRead("flowrateUniformDesign.dat", kFALSE, kTRUE);
URANIE::Relauncher::TLauncher2 lanceur(&tds, run);
// resolution
lanceur.solverLoop();
std::stringstream outname; outname<<"_output_testFlowrateMPIStandalone_"<<initType<<"_.dat";
tds.exportData( outname.str().c_str() );
run->stopSlave();
}
delete run;
return 0;
}
Here there are few differences the most obvious one being the proper C++ structure:
there are several includes that has to be included on top the macro in order to provided the needed headers;
the main function has to follow a classical form
int main(int argc, char **argv)which allows to modify parameters on the fly once compiled (as for theinitTypeinteger here through a dedicated loop). The consequence of this signature will be discussed later on;a
usagefunction has been written in order to provide interactive guidelines on how to deal with this code.
Apart from this, from the variable definition to the assessor description, the structure is similar to the one discussed previously (see Macro). Once this point is reached there are two ways to construct the runner:
with the classical constructor. In this case, as one is not running a ROOT session, the constructor will not found the interactive command parameters and the
MPI_Initwill not automatically be called, thus meaning that one needs to call this method with the proper parameters as done below:
MPI_Init(&argc, &argv);
run = new URANIE::MpiRelauncher::TMpiRun(&mycode);
with the new constructor provided with v4.6.0 whose argument are the usual assessor and the interactive parameters:
run = new URANIE::MpiRelauncher::TMpiRun(&mycode, &argc, &argv);
The final difference is the fact that this code has to be properly compiled, using MPI-complient compilor in order to be able to produce an executable that will be run later on. This can be done like this:
mpicc -o relaunStand relauncherCodeFlowrateMpiStandalone.C `echo $URANIECPPFLAG $URANIELDFLAG` -lstdc++
where mpicc is the c++ mpi-complient compilor, the argument after -o is the executable name,
then one can see the input file while the rest is provinding all compiling and linking flag in order
to reach the goal. Finally, instead of using the classical root -l command, one will use this line
mpirun -np N ./relaunStand initType XX
where the N part should be replaced by the number of requested threads (always strickly greater
than 1) and XX should be replaced either by 0 or 1 depending on whether one wants to use the
classical TMpiRun constructor. Once run, this macro also leads to the following plots (both
initialisation are shown).
13.7.8.3. Graph
Figure 13.51 Representation of the output as a function of the first input with a colZ option when using
either the classical or dedicated constructor