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
48namespace 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 {
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.