Viskores  1.0
AdvectAlgorithmTerminator.h
Go to the documentation of this file.
1 //============================================================================
2 // The contents of this file are covered by the Viskores license. See
3 // LICENSE.txt for details.
4 //
5 // By contributing to this file, all contributors agree to the Developer
6 // Certificate of Origin Version 1.1 (DCO 1.1) as stated in DCO.txt.
7 //============================================================================
8 
9 //============================================================================
10 // Copyright (c) Kitware, Inc.
11 // All rights reserved.
12 // See LICENSE.txt for details.
13 //
14 // This software is distributed WITHOUT ANY WARRANTY; without even
15 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 // PURPOSE. See the above copyright notice for more information.
17 //============================================================================
18 
19 #ifndef viskores_filter_flow_internal_AdvectAlgorithmTerminator_h
20 #define viskores_filter_flow_internal_AdvectAlgorithmTerminator_h
21 
22 namespace viskores
23 {
24 namespace filter
25 {
26 namespace flow
27 {
28 namespace internal
29 {
30 
31 // This is based on:
32 // D. Morozov, et al., "IExchange: Asynchronous Communication and Termination Detection for Iterative Algorithms,"
33 // 2021 IEEE 11th Symposium on Large Data Analysis and Visualization (LDAV), New Orleans, LA, USA, 2021, pp. 12-21,
34 // doi: 10.1109/LDAV53230.2021.00009.
35 //
36 // The challenge for async termination is to determine when all work is complete and no messages remain in flight.
37 // The algorithm uses a number of states to determine when this occurs.
38 // State 0: a process is working.
39 // State 1: Process is done and waiting
40 // State 2: All done and checking for cancelation
41 //
42 // State 0: ----- if no work ----> State 1: (locally done. call ibarrier).
43 // |
44 // | ibarrier done
45 // | dirty = "have new work since entering State 1"
46 // | call iallreduce(dirty)
47 // |
48 // State 2: (all done, checking for cancel)
49 // |
50 // | if dirty == 1 : GOTO State 0.
51 // | else: Done
52 //
53 // A process begins in State 0 and remains until it has no more work to do.
54 // Process calls ibarrier and enters State 1. When the ibarrier is satisfied, this means that all processes are in State 1.
55 // When all processes are in State 1, each process sets a dirty flag to true if any work has arrived since entering State 1.
56 // Each procces call iallreduce(dirty) and enter State 2.
57 // In State 2, if the iallreduce returns true, there is new work, so return to State 0.
58 // If the iallreduce returns false, then all work is complete and we can terminate.
59 //
60 class AdvectAlgorithmTerminator
61 {
62 public:
63 #ifdef VISKORES_ENABLE_MPI
64  AdvectAlgorithmTerminator(viskoresdiy::mpi::communicator& comm)
65  : AllDirty(1)
66  , Dirty(1)
67  , LocalWork(0)
68  , MPIComm(viskoresdiy::mpi::mpi_cast(comm.handle()))
69  , Rank(comm.rank())
70  , State(STATE_0)
71 #else
72  AdvectAlgorithmTerminator(viskoresdiy::mpi::communicator& viskoresNotUsed(comm))
73  : HaveWork(false)
74 #endif
75  {
76  this->FirstCall = true;
77  }
78 
79  bool Done() const
80  {
81 #ifdef VISKORES_ENABLE_MPI
82  return this->State == AdvectAlgorithmTerminatorState::DONE;
83 #else
84  return !this->HaveWork;
85 #endif
86  }
87 
88  void Control(bool haveLocalWork)
89  {
90 #ifdef VISKORES_ENABLE_MPI
91  if (this->FirstCall)
92  {
93  haveLocalWork = true;
94  this->FirstCall = false;
95  }
96 
97  if (haveLocalWork)
98  this->Dirty = 1;
99 
100  if (this->State == STATE_0 && !haveLocalWork)
101  {
102  //No more work for this rank.
103  //Set Dirty = 0 (to see if any work arrives while in STATE_1)
104  MPI_Ibarrier(this->MPIComm, &this->StateReq);
105  this->Dirty = 0;
106  this->State = STATE_1;
107  }
108  else if (this->State == STATE_1)
109  {
110  MPI_Status status;
111  int flag;
112  MPI_Test(&this->StateReq, &flag, &status);
113  if (flag == 1)
114  {
115  this->LocalDirty = this->Dirty;
116  MPI_Iallreduce(
117  &this->LocalDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq);
118  this->State = STATE_2;
119  }
120  }
121  else if (this->State == STATE_2)
122  {
123  MPI_Status status;
124  int flag;
125  MPI_Test(&this->StateReq, &flag, &status);
126  if (flag == 1)
127  {
128  //If no rank has had any new work since the iBarrier, work is complete.
129  //Otherwise, return to STATE_0.
130  if (this->AllDirty == 0)
131  this->State = DONE;
132  else
133  this->State = STATE_0;
134  }
135  }
136 #else
137  this->HaveWork = haveLocalWork;
138 #endif
139  }
140 
141 private:
142  bool FirstCall;
143 
144 #ifdef VISKORES_ENABLE_MPI
145  enum AdvectAlgorithmTerminatorState
146  {
147  STATE_0,
148  STATE_1,
149  STATE_2,
150  DONE
151  };
152 
153  int AllDirty;
154  //Dirty: Has this rank seen any work since entering state?
155  std::atomic<int> Dirty;
156  int LocalDirty;
157  std::atomic<int> LocalWork;
158  MPI_Comm MPIComm;
159  viskores::Id Rank;
160  AdvectAlgorithmTerminatorState State = AdvectAlgorithmTerminatorState::STATE_0;
161  MPI_Request StateReq;
162 #else
163  bool HaveWork;
164 #endif
165 };
166 
167 }
168 }
169 }
170 } //viskores::filter::flow::internal
171 
172 
173 #endif //viskores_filter_flow_internal_AdvectAlgorithmTerminator_h
viskoresNotUsed
#define viskoresNotUsed(parameter_name)
Simple macro to identify a parameter as unused.
Definition: ExportMacros.h:136
viskores::Id
viskores::Int64 Id
Base type to use to index arrays.
Definition: Types.h:235
viskores
Groups connected points that have the same field value.
Definition: Atomic.h:27