commSchedule.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2022-2023 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "commSchedule.H"
30 #include "IOstreams.H"
31 #include "IOmanip.H"
32 #include "StringStream.H"
33 #include "Pstream.H"
34 
35 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
36 
37 namespace Foam
38 {
39  defineTypeNameAndDebug(commSchedule, 0);
40 }
41 
42 
43 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
44 
45 namespace Foam
46 {
47 
48 // Count the number of outstanding communications for a single processor
49 static label outstandingComms
50 (
51  const labelUList& commToSchedule,
52  const DynamicList<label>& procComms
53 )
54 {
55  label nOutstanding = 0;
56 
57  for (const label commPairi : procComms)
58  {
59  if (commToSchedule[commPairi] == -1)
60  {
61  ++nOutstanding;
62  }
63  }
64  return nOutstanding;
65 }
66 
67 } // End namespace Foam
68 
69 
70 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
71 
73 (
74  const label nProcs,
75  const List<labelPair>& comms
76 )
77 :
78  schedule_(comms.size()),
79  procSchedule_(nProcs)
80 {
81  // Determine comms per processor.
82  List<DynamicList<label>> procToComms(nProcs);
83 
84  forAll(comms, commPairi)
85  {
86  const label proc0 = comms[commPairi].first();
87  const label proc1 = comms[commPairi].second();
88 
89  if (proc0 < 0 || proc0 >= nProcs || proc1 < 0 || proc1 >= nProcs)
90  {
92  << "Illegal processor(s): "
93  << comms[commPairi] << abort(FatalError);
94  }
95 
96  procToComms[proc0].push_back(commPairi);
97  procToComms[proc1].push_back(commPairi);
98  }
99  // Note: no need to shrink procToComms. Are small.
100 
101  if (debug && UPstream::master())
102  {
103  Pout<< "commSchedule : Wanted communication:" << endl;
104 
105  forAll(comms, i)
106  {
107  const labelPair& twoProcs = comms[i];
108 
109  Pout<< i << ": "
110  << twoProcs.first() << " <-> " << twoProcs.second() << endl;
111  }
112  Pout<< endl;
113 
114 
115  Pout<< "commSchedule : Schedule:" << endl;
116 
117  // Print header. Use buffered output to prevent parallel output messing
118  // up.
119  {
120  OStringStream os;
121  os << "iter|";
122  for (int i = 0; i < nProcs; i++)
123  {
124  os << setw(3) << i;
125  }
126  Pout<< os.str().c_str() << endl;
127  }
128  {
129  OStringStream os;
130  os << "----+";
131  for (int i = 0; i < nProcs; i++)
132  {
133  os << "---";
134  }
135  Pout<< os.str().c_str() << endl;
136  }
137  }
138 
139  // Schedule all. Note: crap scheduler. Assumes all communication takes
140  // equally long.
141 
142  label nScheduled = 0;
143 
144  label iter = 0;
145 
146  // Per index into comms the time when it was scheduled
147  labelList commToSchedule(comms.size(), -1);
148 
149  while (nScheduled < comms.size())
150  {
151  label oldNScheduled = nScheduled;
152 
153  // Find unscheduled comms. This is the comms where the two processors
154  // still have the most unscheduled comms.
155 
156  boolList busy(nProcs, false);
157 
158  while (true)
159  {
160  label maxComm = -1;
161  label maxNeed = labelMin;
162 
163  forAll(comms, commPairi)
164  {
165  const label proc0 = comms[commPairi].first();
166  const label proc1 = comms[commPairi].second();
167 
168  if
169  (
170  commToSchedule[commPairi] == -1 // unscheduled
171  && !busy[proc0]
172  && !busy[proc1]
173  )
174  {
175  label need =
176  (
177  outstandingComms(commToSchedule, procToComms[proc0])
178  + outstandingComms(commToSchedule, procToComms[proc1])
179  );
180 
181  if (maxNeed < need)
182  {
183  maxNeed = need;
184  maxComm = commPairi;
185  }
186  }
187  }
188 
189 
190  if (maxComm == -1)
191  {
192  // Found no unscheduled procs.
193  break;
194  }
195 
196  // Schedule commPairi in this iteration
197  commToSchedule[maxComm] = nScheduled++;
198  busy[comms[maxComm].first()] = true;
199  busy[comms[maxComm].second()] = true;
200  }
201 
202  if (debug && UPstream::master())
203  {
204  label nIterComms = nScheduled-oldNScheduled;
205 
206  if (nIterComms > 0)
207  {
208  labelList procToComm(nProcs, -1);
209 
210  forAll(commToSchedule, commPairi)
211  {
212  const label sched = commToSchedule[commPairi];
213 
214  if (sched >= oldNScheduled && sched < nScheduled)
215  {
216  const label proc0 = comms[commPairi].first();
217  const label proc1 = comms[commPairi].second();
218  procToComm[proc0] = commPairi;
219  procToComm[proc1] = commPairi;
220  }
221  }
222 
223  // Print it
224  OStringStream os;
225  os << setw(3) << iter << " |";
226  forAll(procToComm, proci)
227  {
228  if (procToComm[proci] == -1)
229  {
230  os << " ";
231  }
232  else
233  {
234  os << setw(3) << procToComm[proci];
235  }
236  }
237  Pout<< os.str().c_str() << endl;
238  }
239  }
240 
241  iter++;
242  }
243 
244  if (debug && UPstream::master())
245  {
246  Pout<< endl;
247  }
248 
249 
250  // Sort commToSchedule to obtain order in comms
251 
252  Foam::sortedOrder(commToSchedule, schedule_);
253 
254  // Sort schedule_ by processor
255 
256  labelList nProcScheduled(nProcs, Zero);
257 
258  // Count
259  for (const label commPairi : schedule_)
260  {
261  const labelPair& twoProcs = comms[commPairi];
262 
263  nProcScheduled[twoProcs.first()]++;
264  nProcScheduled[twoProcs.second()]++;
265  }
266 
267  // Allocate
268  forAll(procSchedule_, proci)
269  {
270  procSchedule_[proci].resize_nocopy(nProcScheduled[proci]);
271  }
272 
273  nProcScheduled = 0;
274 
275  // Fill
276  for (const label commPairi : schedule_)
277  {
278  const labelPair& twoProcs = comms[commPairi];
279 
280  const label proc0 = twoProcs.first();
281  const label proc1 = twoProcs.second();
282 
283  procSchedule_[proc0][nProcScheduled[proc0]++] = commPairi;
284  procSchedule_[proc1][nProcScheduled[proc1]++] = commPairi;
285  }
286 
287  if (debug && UPstream::master())
288  {
289  Pout<< "commSchedule::commSchedule : Per processor:" << endl;
290 
291  forAll(procSchedule_, proci)
292  {
293  const labelList& procComms = procSchedule_[proci];
294 
295  Pout<< "Processor " << proci << " talks to processors:" << endl;
296 
297  for (const label commPairi : procComms)
298  {
299  const labelPair& twoProcs = comms[commPairi];
300 
301  Pout<< " "
302  << (proci == twoProcs[1] ? twoProcs[0] : twoProcs[1])
303  << endl;
304  }
305  }
306  Pout<< endl;
307  }
308 }
309 
310 
311 // ************************************************************************* //
const T & first() const noexcept
Access the first element.
Definition: Pair.H:137
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
Input/output from string buffers.
labelList sortedOrder(const UList< T > &input)
Return the (stable) sort order for the list.
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
T & first()
Access first element of the list, position [0].
Definition: UList.H:853
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
void resize_nocopy(const label len)
Adjust allocated size of list without necessarily.
Definition: ListI.H:175
constexpr label labelMin
Definition: label.H:54
commSchedule(const label nProcs, const List< labelPair > &comms)
Construct from wanted communication. Wanted communication is between two processors. Can be a one-way communication or two-way communication, that is up to the caller.
Definition: commSchedule.C:66
void push_back(const T &val)
Append an element at the end of the list.
Definition: ListI.H:227
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
Useful combination of include files which define Sin, Sout and Serr and the use of IO streams general...
errorManip< error > abort(error &err)
Definition: errorManip.H:139
Istream and Ostream manipulators taking arguments.
int debug
Static debugging option.
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:51
OBJstream os(runTime.globalPath()/outputName)
defineTypeNameAndDebug(combustionModel, 0)
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
List< label > labelList
A List of labels.
Definition: List.H:62
List< bool > boolList
A List of bools.
Definition: List.H:60
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
static label outstandingComms(const labelUList &commToSchedule, const DynamicList< label > &procComms)
Definition: commSchedule.C:43
Namespace for OpenFOAM.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127