Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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 ("Send");
64  sendTypes.push_back ("Alltoall");
65 #if defined(HAVE_TPETRACORE_MPI_ADVANCE)
66  sendTypes.push_back ("MpiAdvanceAlltoall");
67  sendTypes.push_back ("MpiAdvanceNbralltoallv");
68 #endif
69  return sendTypes;
70  }
71 
73  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
74  const Teuchos::RCP<Teuchos::FancyOStream>& /* out */,
75  const Teuchos::RCP<Teuchos::ParameterList>& plist)
76  : plan_(comm)
77  {
78  this->setParameterList(plist);
79  }
80 
82  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm)
83  : Distributor (comm, Teuchos::null, Teuchos::null)
84  {}
85 
87  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
88  const Teuchos::RCP<Teuchos::FancyOStream>& out)
89  : Distributor (comm, out, Teuchos::null)
90  {}
91 
93  Distributor (const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
94  const Teuchos::RCP<Teuchos::ParameterList>& plist)
95  : Distributor (comm, Teuchos::null, plist)
96  {}
97 
99  Distributor (const Distributor& distributor)
100  : plan_(distributor.plan_)
101  , actor_(distributor.actor_)
102  , verbose_ (distributor.verbose_)
103  , reverseDistributor_ (distributor.reverseDistributor_)
104  {
105  using Teuchos::ParameterList;
106  using Teuchos::RCP;
107  using Teuchos::rcp;
108 
109  RCP<const ParameterList> rhsList = distributor.getParameterList ();
110  RCP<ParameterList> newList = rhsList.is_null () ? Teuchos::null :
111  Teuchos::parameterList (*rhsList);
112  this->setParameterList (newList);
113  }
114 
116  using Teuchos::ParameterList;
117  using Teuchos::parameterList;
118  using Teuchos::RCP;
119 
120  std::swap (plan_, rhs.plan_);
121  std::swap (actor_, rhs.actor_);
122  std::swap (verbose_, rhs.verbose_);
123  std::swap (reverseDistributor_, rhs.reverseDistributor_);
124 
125  // Swap parameter lists. If they are the same object, make a deep
126  // copy first, so that modifying one won't modify the other one.
127  RCP<ParameterList> lhsList = this->getNonconstParameterList ();
128  RCP<ParameterList> rhsList = rhs.getNonconstParameterList ();
129  if (lhsList.getRawPtr () == rhsList.getRawPtr () && ! rhsList.is_null ()) {
130  rhsList = parameterList (*rhsList);
131  }
132  if (! rhsList.is_null ()) {
133  this->setMyParamList (rhsList);
134  }
135  if (! lhsList.is_null ()) {
136  rhs.setMyParamList (lhsList);
137  }
138 
139  // We don't need to swap timers, because all instances of
140  // Distributor use the same timers.
141  }
142 
143  bool
144  Distributor::getVerbose()
145  {
146  return Details::Behavior::verbose("Distributor") ||
147  Details::Behavior::verbose("Tpetra::Distributor");
148  }
149 
150  std::unique_ptr<std::string>
151  Distributor::
152  createPrefix(const char methodName[]) const
153  {
154  return Details::createPrefix(
155  plan_.getComm().getRawPtr(), "Distributor", methodName);
156  }
157 
158  void
160  setParameterList (const Teuchos::RCP<Teuchos::ParameterList>& plist)
161  {
162  using ::Tpetra::Details::Behavior;
163  using Teuchos::FancyOStream;
164  using Teuchos::getIntegralValue;
165  using Teuchos::includesVerbLevel;
166  using Teuchos::ParameterList;
167  using Teuchos::parameterList;
168  using Teuchos::RCP;
169  using std::endl;
170 
171  if (! plist.is_null()) {
172  RCP<const ParameterList> validParams = getValidParameters ();
173  plist->validateParametersAndSetDefaults (*validParams);
174 
175  // ParameterListAcceptor semantics require pointer identity of the
176  // sublist passed to setParameterList(), so we save the pointer.
177  this->setMyParamList (plist);
178 
179  RCP<ParameterList> planParams(plist);
180  planParams->remove("Debug", false);
181  planParams->remove("VerboseObject", false);
182  plan_.setParameterList(planParams);
183  }
184  }
185 
186  Teuchos::RCP<const Teuchos::ParameterList>
188  {
189  using Teuchos::Array;
190  using Teuchos::ParameterList;
191  using Teuchos::parameterList;
192  using Teuchos::RCP;
193  using Teuchos::setStringToIntegralParameter;
194 
195  const bool debug = tpetraDistributorDebugDefault;
196 
197  Array<std::string> sendTypes = distributorSendTypes ();
198  const std::string defaultSendType ("Send");
199  Array<Details::EDistributorSendType> sendTypeEnums;
200  sendTypeEnums.push_back (Details::DISTRIBUTOR_ISEND);
201  sendTypeEnums.push_back (Details::DISTRIBUTOR_SEND);
202  sendTypeEnums.push_back (Details::DISTRIBUTOR_ALLTOALL);
203 #if defined(HAVE_TPETRACORE_MPI_ADVANCE)
204  sendTypeEnums.push_back(Details::DISTRIBUTOR_MPIADVANCE_ALLTOALL);
205  sendTypeEnums.push_back(Details::DISTRIBUTOR_MPIADVANCE_NBRALLTOALLV);
206 #endif
207 
208  RCP<ParameterList> plist = parameterList ("Tpetra::Distributor");
209  setStringToIntegralParameter<Details::EDistributorSendType> ("Send type",
210  defaultSendType, "When using MPI, the variant of send to use in "
211  "do[Reverse]Posts()", sendTypes(), sendTypeEnums(), plist.getRawPtr());
212  plist->set ("Debug", debug, "Whether to print copious debugging output on "
213  "all processes.");
214  plist->set ("Timer Label","","Label for Time Monitor output");
215 
216  // mfh 24 Dec 2015: Tpetra no longer inherits from
217  // Teuchos::VerboseObject, so it doesn't need the "VerboseObject"
218  // sublist. However, we retain the "VerboseObject" sublist
219  // anyway, for backwards compatibility (otherwise the above
220  // validation would fail with an invalid parameter name, should
221  // the user still want to provide this list).
222  Teuchos::setupVerboseObjectSublist (&*plist);
223  return Teuchos::rcp_const_cast<const ParameterList> (plist);
224  }
225 
226 
228  { return plan_.getTotalReceiveLength(); }
229 
231  { return plan_.getNumReceives(); }
232 
234  { return plan_.hasSelfMessage(); }
235 
237  { return plan_.getNumSends(); }
238 
240  { return plan_.getMaxSendLength(); }
241 
242  Teuchos::ArrayView<const int> Distributor::getProcsFrom() const
243  { return plan_.getProcsFrom(); }
244 
245  Teuchos::ArrayView<const size_t> Distributor::getLengthsFrom() const
246  { return plan_.getLengthsFrom(); }
247 
248  Teuchos::ArrayView<const int> Distributor::getProcsTo() const
249  { return plan_.getProcsTo(); }
250 
251  Teuchos::ArrayView<const size_t> Distributor::getLengthsTo() const
252  { return plan_.getLengthsTo(); }
253 
254  Teuchos::RCP<Distributor>
255  Distributor::getReverse(bool create) const {
256  if (reverseDistributor_.is_null () && create) {
257  createReverseDistributor ();
258  }
259  TEUCHOS_TEST_FOR_EXCEPTION
260  (reverseDistributor_.is_null () && create, std::logic_error, "The reverse "
261  "Distributor is null after createReverseDistributor returned. "
262  "Please report this bug to the Tpetra developers.");
263  return reverseDistributor_;
264  }
265 
266 
267  void
268  Distributor::createReverseDistributor() const
269  {
270  reverseDistributor_ = Teuchos::rcp(new Distributor(plan_.getComm()));
271  reverseDistributor_->plan_ = *plan_.getReversePlan();
272  reverseDistributor_->verbose_ = verbose_;
273 
274  // requests_: Allocated on demand.
275  // reverseDistributor_: See note below
276 
277  // I am my reverse Distributor's reverse Distributor.
278  // Thus, it would be legit to do the following:
279  //
280  // reverseDistributor_->reverseDistributor_ = Teuchos::rcp (this, false);
281  //
282  // (Note use of a "weak reference" to avoid a circular RCP
283  // dependency.) The only issue is that if users hold on to the
284  // reverse Distributor but let go of the forward one, this
285  // reference won't be valid anymore. However, the reverse
286  // Distributor is really an implementation detail of Distributor
287  // and not meant to be used directly, so we don't need to do this.
288  reverseDistributor_->reverseDistributor_ = Teuchos::null;
289  }
290 
291  void
293  {
294  actor_.doWaits(plan_);
295  }
296 
298  // call doWaits() on the reverse Distributor, if it exists
299  if (! reverseDistributor_.is_null()) {
300  reverseDistributor_->doWaits();
301  }
302  }
303 
304  std::string Distributor::description () const {
305  std::ostringstream out;
306 
307  out << "\"Tpetra::Distributor\": {";
308  const std::string label = this->getObjectLabel ();
309  if (label != "") {
310  out << "Label: " << label << ", ";
311  }
312  out << "How initialized: "
313  << Details::DistributorHowInitializedEnumToString (plan_.howInitialized())
314  << ", Parameters: {"
315  << "Send type: "
316  << DistributorSendTypeEnumToString (plan_.getSendType())
317  << ", Debug: " << (verbose_ ? "true" : "false")
318  << "}}";
319  return out.str ();
320  }
321 
322  std::string
323  Distributor::
324  localDescribeToString (const Teuchos::EVerbosityLevel vl) const
325  {
326  using Teuchos::toString;
327  using Teuchos::VERB_HIGH;
328  using Teuchos::VERB_EXTREME;
329  using std::endl;
330 
331  // This preserves current behavior of Distributor.
332  if (vl <= Teuchos::VERB_LOW || plan_.getComm().is_null ()) {
333  return std::string ();
334  }
335 
336  auto outStringP = Teuchos::rcp (new std::ostringstream ());
337  auto outp = Teuchos::getFancyOStream (outStringP); // returns RCP
338  Teuchos::FancyOStream& out = *outp;
339 
340  const int myRank = plan_.getComm()->getRank ();
341  const int numProcs = plan_.getComm()->getSize ();
342  out << "Process " << myRank << " of " << numProcs << ":" << endl;
343  Teuchos::OSTab tab1 (out);
344 
345  out << "selfMessage: " << hasSelfMessage() << endl;
346  out << "numSends: " << getNumSends() << endl;
347  if (vl == VERB_HIGH || vl == VERB_EXTREME) {
348  out << "procsTo: " << toString (plan_.getProcsTo()) << endl;
349  out << "lengthsTo: " << toString (plan_.getLengthsTo()) << endl;
350  out << "maxSendLength: " << getMaxSendLength() << endl;
351  }
352  if (vl == VERB_EXTREME) {
353  out << "startsTo: " << toString (plan_.getStartsTo()) << endl;
354  out << "indicesTo: " << toString (plan_.getIndicesTo()) << endl;
355  }
356  if (vl == VERB_HIGH || vl == VERB_EXTREME) {
357  out << "numReceives: " << getNumReceives() << endl;
358  out << "totalReceiveLength: " << getTotalReceiveLength() << endl;
359  out << "lengthsFrom: " << toString (plan_.getLengthsFrom()) << endl;
360  out << "procsFrom: " << toString (plan_.getProcsFrom()) << endl;
361  }
362 
363  out.flush (); // make sure the ostringstream got everything
364  return outStringP->str ();
365  }
366 
367  void
369  describe (Teuchos::FancyOStream& out,
370  const Teuchos::EVerbosityLevel verbLevel) const
371  {
372  using std::endl;
373  using Teuchos::VERB_DEFAULT;
374  using Teuchos::VERB_NONE;
375  using Teuchos::VERB_LOW;
376  using Teuchos::VERB_MEDIUM;
377  using Teuchos::VERB_HIGH;
378  using Teuchos::VERB_EXTREME;
379  const Teuchos::EVerbosityLevel vl =
380  (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
381 
382  if (vl == VERB_NONE) {
383  return; // don't print anything
384  }
385  // If this Distributor's Comm is null, then the the calling
386  // process does not participate in Distributor-related collective
387  // operations with the other processes. In that case, it is not
388  // even legal to call this method. The reasonable thing to do in
389  // that case is nothing.
390  if (plan_.getComm().is_null ()) {
391  return;
392  }
393  const int myRank = plan_.getComm()->getRank ();
394  const int numProcs = plan_.getComm()->getSize ();
395 
396  // Only Process 0 should touch the output stream, but this method
397  // in general may need to do communication. Thus, we may need to
398  // preserve the current tab level across multiple "if (myRank ==
399  // 0) { ... }" inner scopes. This is why we sometimes create
400  // OSTab instances by pointer, instead of by value. We only need
401  // to create them by pointer if the tab level must persist through
402  // multiple inner scopes.
403  Teuchos::RCP<Teuchos::OSTab> tab0, tab1;
404 
405  if (myRank == 0) {
406  // At every verbosity level but VERB_NONE, Process 0 prints.
407  // By convention, describe() always begins with a tab before
408  // printing.
409  tab0 = Teuchos::rcp (new Teuchos::OSTab (out));
410  // We quote the class name because it contains colons.
411  // This makes the output valid YAML.
412  out << "\"Tpetra::Distributor\":" << endl;
413  tab1 = Teuchos::rcp (new Teuchos::OSTab (out));
414 
415  const std::string label = this->getObjectLabel ();
416  if (label != "") {
417  out << "Label: " << label << endl;
418  }
419  out << "Number of processes: " << numProcs << endl
420  << "How initialized: "
421  << Details::DistributorHowInitializedEnumToString (plan_.howInitialized())
422  << endl;
423  {
424  out << "Parameters: " << endl;
425  Teuchos::OSTab tab2 (out);
426  out << "\"Send type\": "
427  << DistributorSendTypeEnumToString (plan_.getSendType()) << endl
428  << "\"Debug\": " << (verbose_ ? "true" : "false") << endl;
429  }
430  } // if myRank == 0
431 
432  // This is collective over the Map's communicator.
433  if (vl > VERB_LOW) {
434  const std::string lclStr = this->localDescribeToString (vl);
435  Tpetra::Details::gathervPrint (out, lclStr, *plan_.getComm());
436  }
437 
438  out << "Reverse Distributor:";
439  if (reverseDistributor_.is_null ()) {
440  out << " null" << endl;
441  }
442  else {
443  out << endl;
444  reverseDistributor_->describe (out, vl);
445  }
446  }
447 
448  size_t
450  createFromSends(const Teuchos::ArrayView<const int>& exportProcIDs)
451  {
452  return plan_.createFromSends(exportProcIDs);
453  }
454 
455  void
457  createFromSendsAndRecvs (const Teuchos::ArrayView<const int>& exportProcIDs,
458  const Teuchos::ArrayView<const int>& remoteProcIDs)
459  {
460  plan_.createFromSendsAndRecvs(exportProcIDs, remoteProcIDs);
461  }
462 
463 } // 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.
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.