Tpetra parallel linear algebra  Version of the Day
Tpetra_Distributor.cpp
1 // ***********************************************************************
2 //
3 // Tpetra: Templated Linear Algebra Services Package
4 // Copyright (2008) Sandia Corporation
5 //
6 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7 // the U.S. Government retains certain rights in this software.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are
11 // met:
12 //
13 // 1. Redistributions of source code must retain the above copyright
14 // notice, this list of conditions and the following disclaimer.
15 //
16 // 2. Redistributions in binary form must reproduce the above copyright
17 // notice, this list of conditions and the following disclaimer in the
18 // documentation and/or other materials provided with the distribution.
19 //
20 // 3. Neither the name of the Corporation nor the names of the
21 // contributors may be used to endorse or promote products derived from
22 // this software without specific prior written permission.
23 //
24 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
25 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
28 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 //
36 // ************************************************************************
37 // @HEADER
38 
39 #include "Tpetra_Distributor.hpp"
42 #include "Tpetra_Util.hpp"
43 #include "Tpetra_Details_makeValidVerboseStream.hpp"
44 #include "Teuchos_StandardParameterEntryValidators.hpp"
45 #include "Teuchos_VerboseObjectParameterListHelpers.hpp"
46 #include <numeric>
47 
48 namespace Tpetra {
49  // We set default values of Distributor's Boolean parameters here,
50  // in this one place. That way, if we want to change the default
51  // value of a parameter, we don't have to search the whole file to
52  // ensure a consistent setting.
53  namespace {
54  // Default value of the "Debug" parameter.
55  const bool tpetraDistributorDebugDefault = false;
56  } // namespace (anonymous)
57 
58  Teuchos::Array<std::string>
60  {
61  Teuchos::Array<std::string> sendTypes;
62  sendTypes.push_back ("Isend");
63  sendTypes.push_back ("Rsend");
64  sendTypes.push_back ("Send");
65  sendTypes.push_back ("Ssend");
66  return sendTypes;
67  }
68 
70  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
71  const Teuchos::RCP<Teuchos::FancyOStream>& /* out */,
72  const Teuchos::RCP<Teuchos::ParameterList>& plist)
73  : plan_(comm)
74  , lastRoundBytesSend_ (0)
75  , lastRoundBytesRecv_ (0)
76  {
77  this->setParameterList(plist);
78  }
79 
81  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm)
82  : Distributor (comm, Teuchos::null, Teuchos::null)
83  {}
84 
86  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
87  const Teuchos::RCP<Teuchos::FancyOStream>& out)
88  : Distributor (comm, out, Teuchos::null)
89  {}
90 
92  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
93  const Teuchos::RCP<Teuchos::ParameterList>& plist)
94  : Distributor (comm, Teuchos::null, plist)
95  {}
96 
98  Distributor (const Distributor& distributor)
99  : plan_(distributor.plan_)
100  , actor_(distributor.actor_)
101  , verbose_ (distributor.verbose_)
102  , reverseDistributor_ (distributor.reverseDistributor_)
103  , lastRoundBytesSend_ (distributor.lastRoundBytesSend_)
104  , lastRoundBytesRecv_ (distributor.lastRoundBytesRecv_)
105  {
106  using Teuchos::ParameterList;
107  using Teuchos::RCP;
108  using Teuchos::rcp;
109 
110  RCP<const ParameterList> rhsList = distributor.getParameterList ();
111  RCP<ParameterList> newList = rhsList.is_null () ? Teuchos::null :
112  Teuchos::parameterList (*rhsList);
113  this->setParameterList (newList);
114  }
115 
117  using Teuchos::ParameterList;
118  using Teuchos::parameterList;
119  using Teuchos::RCP;
120 
121  std::swap (plan_, rhs.plan_);
122  std::swap (actor_, rhs.actor_);
123  std::swap (verbose_, rhs.verbose_);
124  std::swap (reverseDistributor_, rhs.reverseDistributor_);
125  std::swap (lastRoundBytesSend_, rhs.lastRoundBytesSend_);
126  std::swap (lastRoundBytesRecv_, rhs.lastRoundBytesRecv_);
127 
128  // Swap parameter lists. If they are the same object, make a deep
129  // copy first, so that modifying one won't modify the other one.
130  RCP<ParameterList> lhsList = this->getNonconstParameterList ();
131  RCP<ParameterList> rhsList = rhs.getNonconstParameterList ();
132  if (lhsList.getRawPtr () == rhsList.getRawPtr () && ! rhsList.is_null ()) {
133  rhsList = parameterList (*rhsList);
134  }
135  if (! rhsList.is_null ()) {
136  this->setMyParamList (rhsList);
137  }
138  if (! lhsList.is_null ()) {
139  rhs.setMyParamList (lhsList);
140  }
141 
142  // We don't need to swap timers, because all instances of
143  // Distributor use the same timers.
144  }
145 
146  bool
147  Distributor::getVerbose()
148  {
149  return Details::Behavior::verbose("Distributor") ||
150  Details::Behavior::verbose("Tpetra::Distributor");
151  }
152 
153  std::unique_ptr<std::string>
154  Distributor::
155  createPrefix(const char methodName[]) const
156  {
157  return Details::createPrefix(
158  plan_.getComm().getRawPtr(), "Distributor", methodName);
159  }
160 
161  void
163  setParameterList (const Teuchos::RCP<Teuchos::ParameterList>& plist)
164  {
165  using ::Tpetra::Details::Behavior;
166  using Teuchos::FancyOStream;
167  using Teuchos::getIntegralValue;
168  using Teuchos::includesVerbLevel;
169  using Teuchos::ParameterList;
170  using Teuchos::parameterList;
171  using Teuchos::RCP;
172  using std::endl;
173 
174  if (! plist.is_null()) {
175  RCP<const ParameterList> validParams = getValidParameters ();
176  plist->validateParametersAndSetDefaults (*validParams);
177 
178  // ParameterListAcceptor semantics require pointer identity of the
179  // sublist passed to setParameterList(), so we save the pointer.
180  this->setMyParamList (plist);
181 
182  RCP<ParameterList> planParams(plist);
183  planParams->remove("Debug", false);
184  planParams->remove("VerboseObject", false);
185  plan_.setParameterList(planParams);
186  }
187  }
188 
189  Teuchos::RCP<const Teuchos::ParameterList>
191  {
192  using Teuchos::Array;
193  using Teuchos::ParameterList;
194  using Teuchos::parameterList;
195  using Teuchos::RCP;
196  using Teuchos::setStringToIntegralParameter;
197 
198  const bool barrierBetween = Details::barrierBetween_default;
199  const bool useDistinctTags = Details::useDistinctTags_default;
200  const bool debug = tpetraDistributorDebugDefault;
201 
202  Array<std::string> sendTypes = distributorSendTypes ();
203  const std::string defaultSendType ("Send");
204  Array<Details::EDistributorSendType> sendTypeEnums;
205  sendTypeEnums.push_back (Details::DISTRIBUTOR_ISEND);
206  sendTypeEnums.push_back (Details::DISTRIBUTOR_RSEND);
207  sendTypeEnums.push_back (Details::DISTRIBUTOR_SEND);
208  sendTypeEnums.push_back (Details::DISTRIBUTOR_SSEND);
209 
210  RCP<ParameterList> plist = parameterList ("Tpetra::Distributor");
211  plist->set ("Barrier between receives and sends", barrierBetween,
212  "Whether to execute a barrier between receives and sends in do"
213  "[Reverse]Posts(). Required for correctness when \"Send type\""
214  "=\"Rsend\", otherwise correct but not recommended.");
215  setStringToIntegralParameter<Details::EDistributorSendType> ("Send type",
216  defaultSendType, "When using MPI, the variant of send to use in "
217  "do[Reverse]Posts()", sendTypes(), sendTypeEnums(), plist.getRawPtr());
218  plist->set ("Use distinct tags", useDistinctTags, "Whether to use distinct "
219  "MPI message tags for different code paths. Highly recommended"
220  " to avoid message collisions.");
221  plist->set ("Debug", debug, "Whether to print copious debugging output on "
222  "all processes.");
223  plist->set ("Timer Label","","Label for Time Monitor output");
224  plist->set ("Enable MPI CUDA RDMA support", true, "Assume that MPI can "
225  "tell whether a pointer points to host memory or CUDA device "
226  "memory. You don't need to specify this option any more; "
227  "Tpetra assumes it is always true. This is a very light "
228  "assumption on the MPI implementation, and in fact does not "
229  "actually involve hardware or system RDMA support.");
230 
231  // mfh 24 Dec 2015: Tpetra no longer inherits from
232  // Teuchos::VerboseObject, so it doesn't need the "VerboseObject"
233  // sublist. However, we retain the "VerboseObject" sublist
234  // anyway, for backwards compatibility (otherwise the above
235  // validation would fail with an invalid parameter name, should
236  // the user still want to provide this list).
237  Teuchos::setupVerboseObjectSublist (&*plist);
238  return Teuchos::rcp_const_cast<const ParameterList> (plist);
239  }
240 
241 
243  { return plan_.getTotalReceiveLength(); }
244 
246  { return plan_.getNumReceives(); }
247 
249  { return plan_.hasSelfMessage(); }
250 
252  { return plan_.getNumSends(); }
253 
255  { return plan_.getMaxSendLength(); }
256 
257  Teuchos::ArrayView<const int> Distributor::getProcsFrom() const
258  { return plan_.getProcsFrom(); }
259 
260  Teuchos::ArrayView<const size_t> Distributor::getLengthsFrom() const
261  { return plan_.getLengthsFrom(); }
262 
263  Teuchos::ArrayView<const int> Distributor::getProcsTo() const
264  { return plan_.getProcsTo(); }
265 
266  Teuchos::ArrayView<const size_t> Distributor::getLengthsTo() const
267  { return plan_.getLengthsTo(); }
268 
269  Teuchos::RCP<Distributor>
270  Distributor::getReverse(bool create) const {
271  if (reverseDistributor_.is_null () && create) {
272  createReverseDistributor ();
273  }
274  TEUCHOS_TEST_FOR_EXCEPTION
275  (reverseDistributor_.is_null () && create, std::logic_error, "The reverse "
276  "Distributor is null after createReverseDistributor returned. "
277  "Please report this bug to the Tpetra developers.");
278  return reverseDistributor_;
279  }
280 
281 
282  void
283  Distributor::createReverseDistributor() const
284  {
285  reverseDistributor_ = Teuchos::rcp(new Distributor(plan_.getComm()));
286  reverseDistributor_->plan_ = *plan_.getReversePlan();
287  reverseDistributor_->verbose_ = verbose_;
288 
289  // requests_: Allocated on demand.
290  // reverseDistributor_: See note below
291 
292  // mfh 31 Mar 2016: These are statistics, kept on calls to
293  // doPostsAndWaits or doReversePostsAndWaits. They weren't here
294  // when I started, and I didn't add them, so I don't know if they
295  // are accurate.
296  reverseDistributor_->lastRoundBytesSend_ = 0;
297  reverseDistributor_->lastRoundBytesRecv_ = 0;
298 
299  // I am my reverse Distributor's reverse Distributor.
300  // Thus, it would be legit to do the following:
301  //
302  // reverseDistributor_->reverseDistributor_ = Teuchos::rcp (this, false);
303  //
304  // (Note use of a "weak reference" to avoid a circular RCP
305  // dependency.) The only issue is that if users hold on to the
306  // reverse Distributor but let go of the forward one, this
307  // reference won't be valid anymore. However, the reverse
308  // Distributor is really an implementation detail of Distributor
309  // and not meant to be used directly, so we don't need to do this.
310  reverseDistributor_->reverseDistributor_ = Teuchos::null;
311  }
312 
313  void
315  {
316  actor_.doWaits(plan_);
317  }
318 
320  // call doWaits() on the reverse Distributor, if it exists
321  if (! reverseDistributor_.is_null()) {
322  reverseDistributor_->doWaits();
323  }
324  }
325 
326  std::string Distributor::description () const {
327  std::ostringstream out;
328 
329  out << "\"Tpetra::Distributor\": {";
330  const std::string label = this->getObjectLabel ();
331  if (label != "") {
332  out << "Label: " << label << ", ";
333  }
334  out << "How initialized: "
335  << Details::DistributorHowInitializedEnumToString (plan_.howInitialized())
336  << ", Parameters: {"
337  << "Send type: "
338  << DistributorSendTypeEnumToString (plan_.getSendType())
339  << ", Barrier between receives and sends: "
340  << (plan_.barrierBetweenRecvSend() ? "true" : "false")
341  << ", Use distinct tags: "
342  << (plan_.useDistinctTags() ? "true" : "false")
343  << ", Debug: " << (verbose_ ? "true" : "false")
344  << "}}";
345  return out.str ();
346  }
347 
348  std::string
349  Distributor::
350  localDescribeToString (const Teuchos::EVerbosityLevel vl) const
351  {
352  using Teuchos::toString;
353  using Teuchos::VERB_HIGH;
354  using Teuchos::VERB_EXTREME;
355  using std::endl;
356 
357  // This preserves current behavior of Distributor.
358  if (vl <= Teuchos::VERB_LOW || plan_.getComm().is_null ()) {
359  return std::string ();
360  }
361 
362  auto outStringP = Teuchos::rcp (new std::ostringstream ());
363  auto outp = Teuchos::getFancyOStream (outStringP); // returns RCP
364  Teuchos::FancyOStream& out = *outp;
365 
366  const int myRank = plan_.getComm()->getRank ();
367  const int numProcs = plan_.getComm()->getSize ();
368  out << "Process " << myRank << " of " << numProcs << ":" << endl;
369  Teuchos::OSTab tab1 (out);
370 
371  out << "selfMessage: " << hasSelfMessage() << endl;
372  out << "numSends: " << getNumSends() << endl;
373  if (vl == VERB_HIGH || vl == VERB_EXTREME) {
374  out << "procsTo: " << toString (plan_.getProcsTo()) << endl;
375  out << "lengthsTo: " << toString (plan_.getLengthsTo()) << endl;
376  out << "maxSendLength: " << getMaxSendLength() << endl;
377  }
378  if (vl == VERB_EXTREME) {
379  out << "startsTo: " << toString (plan_.getStartsTo()) << endl;
380  out << "indicesTo: " << toString (plan_.getIndicesTo()) << endl;
381  }
382  if (vl == VERB_HIGH || vl == VERB_EXTREME) {
383  out << "numReceives: " << getNumReceives() << endl;
384  out << "totalReceiveLength: " << getTotalReceiveLength() << endl;
385  out << "lengthsFrom: " << toString (plan_.getLengthsFrom()) << endl;
386  out << "procsFrom: " << toString (plan_.getProcsFrom()) << endl;
387  }
388 
389  out.flush (); // make sure the ostringstream got everything
390  return outStringP->str ();
391  }
392 
393  void
395  describe (Teuchos::FancyOStream& out,
396  const Teuchos::EVerbosityLevel verbLevel) const
397  {
398  using std::endl;
399  using Teuchos::VERB_DEFAULT;
400  using Teuchos::VERB_NONE;
401  using Teuchos::VERB_LOW;
402  using Teuchos::VERB_MEDIUM;
403  using Teuchos::VERB_HIGH;
404  using Teuchos::VERB_EXTREME;
405  const Teuchos::EVerbosityLevel vl =
406  (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
407 
408  if (vl == VERB_NONE) {
409  return; // don't print anything
410  }
411  // If this Distributor's Comm is null, then the the calling
412  // process does not participate in Distributor-related collective
413  // operations with the other processes. In that case, it is not
414  // even legal to call this method. The reasonable thing to do in
415  // that case is nothing.
416  if (plan_.getComm().is_null ()) {
417  return;
418  }
419  const int myRank = plan_.getComm()->getRank ();
420  const int numProcs = plan_.getComm()->getSize ();
421 
422  // Only Process 0 should touch the output stream, but this method
423  // in general may need to do communication. Thus, we may need to
424  // preserve the current tab level across multiple "if (myRank ==
425  // 0) { ... }" inner scopes. This is why we sometimes create
426  // OSTab instances by pointer, instead of by value. We only need
427  // to create them by pointer if the tab level must persist through
428  // multiple inner scopes.
429  Teuchos::RCP<Teuchos::OSTab> tab0, tab1;
430 
431  if (myRank == 0) {
432  // At every verbosity level but VERB_NONE, Process 0 prints.
433  // By convention, describe() always begins with a tab before
434  // printing.
435  tab0 = Teuchos::rcp (new Teuchos::OSTab (out));
436  // We quote the class name because it contains colons.
437  // This makes the output valid YAML.
438  out << "\"Tpetra::Distributor\":" << endl;
439  tab1 = Teuchos::rcp (new Teuchos::OSTab (out));
440 
441  const std::string label = this->getObjectLabel ();
442  if (label != "") {
443  out << "Label: " << label << endl;
444  }
445  out << "Number of processes: " << numProcs << endl
446  << "How initialized: "
447  << Details::DistributorHowInitializedEnumToString (plan_.howInitialized())
448  << endl;
449  {
450  out << "Parameters: " << endl;
451  Teuchos::OSTab tab2 (out);
452  out << "\"Send type\": "
453  << DistributorSendTypeEnumToString (plan_.getSendType()) << endl
454  << "\"Barrier between receives and sends\": "
455  << (plan_.barrierBetweenRecvSend() ? "true" : "false") << endl
456  << "\"Use distinct tags\": "
457  << (plan_.useDistinctTags() ? "true" : "false") << endl
458  << "\"Debug\": " << (verbose_ ? "true" : "false") << endl;
459  }
460  } // if myRank == 0
461 
462  // This is collective over the Map's communicator.
463  if (vl > VERB_LOW) {
464  const std::string lclStr = this->localDescribeToString (vl);
465  Tpetra::Details::gathervPrint (out, lclStr, *plan_.getComm());
466  }
467 
468  out << "Reverse Distributor:";
469  if (reverseDistributor_.is_null ()) {
470  out << " null" << endl;
471  }
472  else {
473  out << endl;
474  reverseDistributor_->describe (out, vl);
475  }
476  }
477 
478  size_t
480  createFromSends(const Teuchos::ArrayView<const int>& exportProcIDs)
481  {
482  return plan_.createFromSends(exportProcIDs);
483  }
484 
485  void
487  createFromSendsAndRecvs (const Teuchos::ArrayView<const int>& exportProcIDs,
488  const Teuchos::ArrayView<const int>& remoteProcIDs)
489  {
490  plan_.createFromSendsAndRecvs(exportProcIDs, remoteProcIDs);
491  }
492 
493 } // namespace Tpetra
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra's behavior.
Declaration of a function that prints strings from each process.
Stand-alone utility functions and macros.
static bool verbose()
Whether Tpetra is in verbose mode.
Sets up and executes a communication plan for a Tpetra DistObject.
size_t getMaxSendLength() const
Maximum number of values this process will send to another single process.
Teuchos::RCP< Distributor > getReverse(bool create=true) const
A reverse communication plan Distributor.
Teuchos::ArrayView< const int > getProcsTo() const
Ranks of the processes to which this process will send values.
size_t getNumReceives() const
The number of processes from which we will receive data.
void setParameterList(const Teuchos::RCP< Teuchos::ParameterList > &plist)
Set Distributor parameters.
size_t getTotalReceiveLength() const
Total number of values this process will receive from other processes.
bool hasSelfMessage() const
Whether the calling process will send or receive messages to itself.
void swap(Distributor &rhs)
Swap the contents of rhs with those of *this.
Teuchos::ArrayView< const size_t > getLengthsTo() const
Number of values this process will send to each process.
Teuchos::ArrayView< const int > getProcsFrom() const
Ranks of the processes sending values to this process.
Distributor(const Teuchos::RCP< const Teuchos::Comm< int > > &comm)
Construct using the specified communicator and default parameters.
std::string description() const
Return a one-line description of this object.
size_t createFromSends(const Teuchos::ArrayView< const int > &exportProcIDs)
Set up Distributor using list of process ranks to which this process will send.
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...
Teuchos::RCP< const Teuchos::ParameterList > getValidParameters() const
List of valid Distributor parameters.
Teuchos::ArrayView< const size_t > getLengthsFrom() const
Number of values this process will receive from each process.
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.
std::unique_ptr< std::string > createPrefix(const int myRank, const char prefix[])
Create string prefix for each line of verbose output.
std::string DistributorHowInitializedEnumToString(EDistributorHowInitialized how)
Convert an EDistributorHowInitialized enum value to a string.
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,...
Namespace Tpetra contains the class and methods constituting the Tpetra library.
Teuchos::Array< std::string > distributorSendTypes()
Valid values for Distributor's "Send type" parameter.