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