Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Tpetra_Distributor.cpp
1 // @HEADER
2 // *****************************************************************************
3 // Tpetra: Templated Linear Algebra Services Package
4 //
5 // Copyright 2008 NTESS and the Tpetra contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #include "Tpetra_Distributor.hpp"
13 #include "Tpetra_Util.hpp"
14 #include "Tpetra_Details_makeValidVerboseStream.hpp"
15 #include "Teuchos_StandardParameterEntryValidators.hpp"
16 #include "Teuchos_VerboseObjectParameterListHelpers.hpp"
17 #include <numeric>
18 
19 namespace Tpetra {
20  // We set default values of Distributor's Boolean parameters here,
21  // in this one place. That way, if we want to change the default
22  // value of a parameter, we don't have to search the whole file to
23  // ensure a consistent setting.
24  namespace {
25  // Default value of the "Debug" parameter.
26  const bool tpetraDistributorDebugDefault = false;
27  } // namespace (anonymous)
28 
29  Teuchos::Array<std::string>
31  {
32  Teuchos::Array<std::string> sendTypes;
33  sendTypes.push_back ("Isend");
34  sendTypes.push_back ("Send");
35  sendTypes.push_back ("Alltoall");
36 #if defined(HAVE_TPETRACORE_MPI_ADVANCE)
37  sendTypes.push_back ("MpiAdvanceAlltoall");
38  sendTypes.push_back ("MpiAdvanceNbralltoallv");
39 #endif
40  return sendTypes;
41  }
42 
44  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
45  const Teuchos::RCP<Teuchos::FancyOStream>& /* out */,
46  const Teuchos::RCP<Teuchos::ParameterList>& plist)
47  : plan_(comm)
48  {
49  this->setParameterList(plist);
50  }
51 
53  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm)
54  : Distributor (comm, Teuchos::null, Teuchos::null)
55  {}
56 
58  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
59  const Teuchos::RCP<Teuchos::FancyOStream>& out)
60  : Distributor (comm, out, Teuchos::null)
61  {}
62 
64  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
65  const Teuchos::RCP<Teuchos::ParameterList>& plist)
66  : Distributor (comm, Teuchos::null, plist)
67  {}
68 
70  Distributor (const Distributor& distributor)
71  : plan_(distributor.plan_)
72  , actor_(distributor.actor_)
73  , verbose_ (distributor.verbose_)
74  , reverseDistributor_ (distributor.reverseDistributor_)
75  {
76  using Teuchos::ParameterList;
77  using Teuchos::RCP;
78  using Teuchos::rcp;
79 
80  RCP<const ParameterList> rhsList = distributor.getParameterList ();
81  RCP<ParameterList> newList = rhsList.is_null () ? Teuchos::null :
82  Teuchos::parameterList (*rhsList);
83  this->setParameterList (newList);
84  }
85 
87  using Teuchos::ParameterList;
88  using Teuchos::parameterList;
89  using Teuchos::RCP;
90 
91  std::swap (plan_, rhs.plan_);
92  std::swap (actor_, rhs.actor_);
93  std::swap (verbose_, rhs.verbose_);
94  std::swap (reverseDistributor_, rhs.reverseDistributor_);
95 
96  // Swap parameter lists. If they are the same object, make a deep
97  // copy first, so that modifying one won't modify the other one.
98  RCP<ParameterList> lhsList = this->getNonconstParameterList ();
99  RCP<ParameterList> rhsList = rhs.getNonconstParameterList ();
100  if (lhsList.getRawPtr () == rhsList.getRawPtr () && ! rhsList.is_null ()) {
101  rhsList = parameterList (*rhsList);
102  }
103  if (! rhsList.is_null ()) {
104  this->setMyParamList (rhsList);
105  }
106  if (! lhsList.is_null ()) {
107  rhs.setMyParamList (lhsList);
108  }
109 
110  // We don't need to swap timers, because all instances of
111  // Distributor use the same timers.
112  }
113 
114  bool
115  Distributor::getVerbose()
116  {
117  return Details::Behavior::verbose("Distributor") ||
118  Details::Behavior::verbose("Tpetra::Distributor");
119  }
120 
121  std::unique_ptr<std::string>
122  Distributor::
123  createPrefix(const char methodName[]) const
124  {
125  return Details::createPrefix(
126  plan_.getComm().getRawPtr(), "Distributor", methodName);
127  }
128 
129  void
131  setParameterList (const Teuchos::RCP<Teuchos::ParameterList>& plist)
132  {
133  using ::Tpetra::Details::Behavior;
134  using Teuchos::FancyOStream;
135  using Teuchos::getIntegralValue;
136  using Teuchos::includesVerbLevel;
137  using Teuchos::ParameterList;
138  using Teuchos::parameterList;
139  using Teuchos::RCP;
140  using std::endl;
141 
142  if (! plist.is_null()) {
143  RCP<const ParameterList> validParams = getValidParameters ();
144  plist->validateParametersAndSetDefaults (*validParams);
145 
146  // ParameterListAcceptor semantics require pointer identity of the
147  // sublist passed to setParameterList(), so we save the pointer.
148  this->setMyParamList (plist);
149 
150  RCP<ParameterList> planParams(plist);
151  planParams->remove("Debug", false);
152  planParams->remove("VerboseObject", false);
153  plan_.setParameterList(planParams);
154  }
155  }
156 
157  Teuchos::RCP<const Teuchos::ParameterList>
159  {
160  using Teuchos::Array;
161  using Teuchos::ParameterList;
162  using Teuchos::parameterList;
163  using Teuchos::RCP;
164  using Teuchos::setStringToIntegralParameter;
165 
166  const bool debug = tpetraDistributorDebugDefault;
167 
168  Array<std::string> sendTypes = distributorSendTypes ();
169  const std::string defaultSendType ("Send");
170  Array<Details::EDistributorSendType> sendTypeEnums;
171  sendTypeEnums.push_back (Details::DISTRIBUTOR_ISEND);
172  sendTypeEnums.push_back (Details::DISTRIBUTOR_SEND);
173  sendTypeEnums.push_back (Details::DISTRIBUTOR_ALLTOALL);
174 #if defined(HAVE_TPETRACORE_MPI_ADVANCE)
175  sendTypeEnums.push_back(Details::DISTRIBUTOR_MPIADVANCE_ALLTOALL);
176  sendTypeEnums.push_back(Details::DISTRIBUTOR_MPIADVANCE_NBRALLTOALLV);
177 #endif
178 
179  RCP<ParameterList> plist = parameterList ("Tpetra::Distributor");
180  setStringToIntegralParameter<Details::EDistributorSendType> ("Send type",
181  defaultSendType, "When using MPI, the variant of send to use in "
182  "do[Reverse]Posts()", sendTypes(), sendTypeEnums(), plist.getRawPtr());
183  plist->set ("Debug", debug, "Whether to print copious debugging output on "
184  "all processes.");
185  plist->set ("Timer Label","","Label for Time Monitor output");
186 
187  // mfh 24 Dec 2015: Tpetra no longer inherits from
188  // Teuchos::VerboseObject, so it doesn't need the "VerboseObject"
189  // sublist. However, we retain the "VerboseObject" sublist
190  // anyway, for backwards compatibility (otherwise the above
191  // validation would fail with an invalid parameter name, should
192  // the user still want to provide this list).
193  Teuchos::setupVerboseObjectSublist (&*plist);
194  return Teuchos::rcp_const_cast<const ParameterList> (plist);
195  }
196 
197 
199  { return plan_.getTotalReceiveLength(); }
200 
202  { return plan_.getNumReceives(); }
203 
205  { return plan_.hasSelfMessage(); }
206 
208  { return plan_.getNumSends(); }
209 
211  { return plan_.getMaxSendLength(); }
212 
213  Teuchos::ArrayView<const int> Distributor::getProcsFrom() const
214  { return plan_.getProcsFrom(); }
215 
216  Teuchos::ArrayView<const size_t> Distributor::getLengthsFrom() const
217  { return plan_.getLengthsFrom(); }
218 
219  Teuchos::ArrayView<const int> Distributor::getProcsTo() const
220  { return plan_.getProcsTo(); }
221 
222  Teuchos::ArrayView<const size_t> Distributor::getLengthsTo() const
223  { return plan_.getLengthsTo(); }
224 
225  Teuchos::RCP<Distributor>
226  Distributor::getReverse(bool create) const {
227  if (reverseDistributor_.is_null () && create) {
228  createReverseDistributor ();
229  }
230  TEUCHOS_TEST_FOR_EXCEPTION
231  (reverseDistributor_.is_null () && create, std::logic_error, "The reverse "
232  "Distributor is null after createReverseDistributor returned. "
233  "Please report this bug to the Tpetra developers.");
234  return reverseDistributor_;
235  }
236 
237 
238  void
239  Distributor::createReverseDistributor() const
240  {
241  reverseDistributor_ = Teuchos::rcp(new Distributor(plan_.getComm()));
242  reverseDistributor_->plan_ = *plan_.getReversePlan();
243  reverseDistributor_->verbose_ = verbose_;
244 
245  // requests_: Allocated on demand.
246  // reverseDistributor_: See note below
247 
248  // I am my reverse Distributor's reverse Distributor.
249  // Thus, it would be legit to do the following:
250  //
251  // reverseDistributor_->reverseDistributor_ = Teuchos::rcp (this, false);
252  //
253  // (Note use of a "weak reference" to avoid a circular RCP
254  // dependency.) The only issue is that if users hold on to the
255  // reverse Distributor but let go of the forward one, this
256  // reference won't be valid anymore. However, the reverse
257  // Distributor is really an implementation detail of Distributor
258  // and not meant to be used directly, so we don't need to do this.
259  reverseDistributor_->reverseDistributor_ = Teuchos::null;
260  }
261 
262  void
264  {
265  actor_.doWaits(plan_);
266  }
267 
269  // call doWaits() on the reverse Distributor, if it exists
270  if (! reverseDistributor_.is_null()) {
271  reverseDistributor_->doWaits();
272  }
273  }
274 
275  std::string Distributor::description () const {
276  std::ostringstream out;
277 
278  out << "\"Tpetra::Distributor\": {";
279  const std::string label = this->getObjectLabel ();
280  if (label != "") {
281  out << "Label: " << label << ", ";
282  }
283  out << "How initialized: "
284  << Details::DistributorHowInitializedEnumToString (plan_.howInitialized())
285  << ", Parameters: {"
286  << "Send type: "
287  << DistributorSendTypeEnumToString (plan_.getSendType())
288  << ", Debug: " << (verbose_ ? "true" : "false")
289  << "}}";
290  return out.str ();
291  }
292 
293  std::string
294  Distributor::
295  localDescribeToString (const Teuchos::EVerbosityLevel vl) const
296  {
297  using Teuchos::toString;
298  using Teuchos::VERB_HIGH;
299  using Teuchos::VERB_EXTREME;
300  using std::endl;
301 
302  // This preserves current behavior of Distributor.
303  if (vl <= Teuchos::VERB_LOW || plan_.getComm().is_null ()) {
304  return std::string ();
305  }
306 
307  auto outStringP = Teuchos::rcp (new std::ostringstream ());
308  auto outp = Teuchos::getFancyOStream (outStringP); // returns RCP
309  Teuchos::FancyOStream& out = *outp;
310 
311  const int myRank = plan_.getComm()->getRank ();
312  const int numProcs = plan_.getComm()->getSize ();
313  out << "Process " << myRank << " of " << numProcs << ":" << endl;
314  Teuchos::OSTab tab1 (out);
315 
316  out << "selfMessage: " << hasSelfMessage() << endl;
317  out << "numSends: " << getNumSends() << endl;
318  if (vl == VERB_HIGH || vl == VERB_EXTREME) {
319  out << "procsTo: " << toString (plan_.getProcsTo()) << endl;
320  out << "lengthsTo: " << toString (plan_.getLengthsTo()) << endl;
321  out << "maxSendLength: " << getMaxSendLength() << endl;
322  }
323  if (vl == VERB_EXTREME) {
324  out << "startsTo: " << toString (plan_.getStartsTo()) << endl;
325  out << "indicesTo: " << toString (plan_.getIndicesTo()) << endl;
326  }
327  if (vl == VERB_HIGH || vl == VERB_EXTREME) {
328  out << "numReceives: " << getNumReceives() << endl;
329  out << "totalReceiveLength: " << getTotalReceiveLength() << endl;
330  out << "lengthsFrom: " << toString (plan_.getLengthsFrom()) << endl;
331  out << "procsFrom: " << toString (plan_.getProcsFrom()) << endl;
332  }
333 
334  out.flush (); // make sure the ostringstream got everything
335  return outStringP->str ();
336  }
337 
338  void
340  describe (Teuchos::FancyOStream& out,
341  const Teuchos::EVerbosityLevel verbLevel) const
342  {
343  using std::endl;
344  using Teuchos::VERB_DEFAULT;
345  using Teuchos::VERB_NONE;
346  using Teuchos::VERB_LOW;
347  using Teuchos::VERB_MEDIUM;
348  using Teuchos::VERB_HIGH;
349  using Teuchos::VERB_EXTREME;
350  const Teuchos::EVerbosityLevel vl =
351  (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
352 
353  if (vl == VERB_NONE) {
354  return; // don't print anything
355  }
356  // If this Distributor's Comm is null, then the the calling
357  // process does not participate in Distributor-related collective
358  // operations with the other processes. In that case, it is not
359  // even legal to call this method. The reasonable thing to do in
360  // that case is nothing.
361  if (plan_.getComm().is_null ()) {
362  return;
363  }
364  const int myRank = plan_.getComm()->getRank ();
365  const int numProcs = plan_.getComm()->getSize ();
366 
367  // Only Process 0 should touch the output stream, but this method
368  // in general may need to do communication. Thus, we may need to
369  // preserve the current tab level across multiple "if (myRank ==
370  // 0) { ... }" inner scopes. This is why we sometimes create
371  // OSTab instances by pointer, instead of by value. We only need
372  // to create them by pointer if the tab level must persist through
373  // multiple inner scopes.
374  Teuchos::RCP<Teuchos::OSTab> tab0, tab1;
375 
376  if (myRank == 0) {
377  // At every verbosity level but VERB_NONE, Process 0 prints.
378  // By convention, describe() always begins with a tab before
379  // printing.
380  tab0 = Teuchos::rcp (new Teuchos::OSTab (out));
381  // We quote the class name because it contains colons.
382  // This makes the output valid YAML.
383  out << "\"Tpetra::Distributor\":" << endl;
384  tab1 = Teuchos::rcp (new Teuchos::OSTab (out));
385 
386  const std::string label = this->getObjectLabel ();
387  if (label != "") {
388  out << "Label: " << label << endl;
389  }
390  out << "Number of processes: " << numProcs << endl
391  << "How initialized: "
392  << Details::DistributorHowInitializedEnumToString (plan_.howInitialized())
393  << endl;
394  {
395  out << "Parameters: " << endl;
396  Teuchos::OSTab tab2 (out);
397  out << "\"Send type\": "
398  << DistributorSendTypeEnumToString (plan_.getSendType()) << endl
399  << "\"Debug\": " << (verbose_ ? "true" : "false") << endl;
400  }
401  } // if myRank == 0
402 
403  // This is collective over the Map's communicator.
404  if (vl > VERB_LOW) {
405  const std::string lclStr = this->localDescribeToString (vl);
406  Tpetra::Details::gathervPrint (out, lclStr, *plan_.getComm());
407  }
408 
409  out << "Reverse Distributor:";
410  if (reverseDistributor_.is_null ()) {
411  out << " null" << endl;
412  }
413  else {
414  out << endl;
415  reverseDistributor_->describe (out, vl);
416  }
417  }
418 
419  size_t
421  createFromSends(const Teuchos::ArrayView<const int>& exportProcIDs)
422  {
423  return plan_.createFromSends(exportProcIDs);
424  }
425 
426  void
428  createFromSendsAndRecvs (const Teuchos::ArrayView<const int>& exportProcIDs,
429  const Teuchos::ArrayView<const int>& remoteProcIDs)
430  {
431  plan_.createFromSendsAndRecvs(exportProcIDs, remoteProcIDs);
432  }
433 
434 } // namespace Tpetra
size_t getNumReceives() const
The number of processes from which we will receive data.
std::string description() const
Return a one-line description of this object.
Teuchos::RCP< Distributor > getReverse(bool create=true) const
A reverse communication plan Distributor.
Declaration of a function that prints strings from each process.
Teuchos::RCP< const Teuchos::ParameterList > getValidParameters() const
List of valid Distributor parameters.
void swap(Distributor &rhs)
Swap the contents of rhs with those of *this.
Teuchos::ArrayView< const size_t > getLengthsFrom() const
Number of values this process will receive from each process.
Teuchos::ArrayView< const int > getProcsFrom() const
Ranks of the processes sending values to this process.
size_t createFromSends(const Teuchos::ArrayView< const int > &exportProcIDs)
Set up Distributor using list of process ranks to which this process will send.
void gathervPrint(std::ostream &out, const std::string &s, const Teuchos::Comm< int > &comm)
On Process 0 in the given communicator, print strings from each process in that communicator, in rank order.
Teuchos::ArrayView< const int > getProcsTo() const
Ranks of the processes to which this process will send values.
void createFromSendsAndRecvs(const Teuchos::ArrayView< const int > &exportProcIDs, const Teuchos::ArrayView< const int > &remoteProcIDs)
Set up Distributor using list of process ranks to which to send, and list of process ranks from which...
bool hasSelfMessage() const
Whether the calling process will send or receive messages to itself.
Sets up and executes a communication plan for a Tpetra DistObject.
static bool verbose()
Whether Tpetra is in verbose mode.
size_t getTotalReceiveLength() const
Total number of values this process will receive from other processes.
void setParameterList(const Teuchos::RCP< Teuchos::ParameterList > &plist)
Set Distributor parameters.
Teuchos::ArrayView< const size_t > getLengthsTo() const
Number of values this process will send to each process.
Teuchos::Array< std::string > distributorSendTypes()
Valid values for Distributor&#39;s &quot;Send type&quot; parameter.
std::string DistributorHowInitializedEnumToString(EDistributorHowInitialized how)
Convert an EDistributorHowInitialized enum value to a string.
Stand-alone utility functions and macros.
size_t getNumSends() const
The number of processes to which we will send data.
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const
Describe this object in a human-readable way to the given output stream.
size_t getMaxSendLength() const
Maximum number of values this process will send to another single process.
std::unique_ptr< std::string > createPrefix(const int myRank, const char prefix[])
Create string prefix for each line of verbose output.
Definition: Tpetra_Util.cpp:71
Distributor(const Teuchos::RCP< const Teuchos::Comm< int > > &comm)
Construct using the specified communicator and default parameters.
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra&#39;s behavior.