OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
MasterNode.h
Go to the documentation of this file.
1//
2// Class MasterNode
3// Implements a node in the network of all pilots, exposing store and
4// collect operations on a specific set of neighbors.
5//
6// Using the neighbor strategy a set of neighbors we collect solution state
7// from (and they collect from us) is defined. Using this set of neighbors the
8// solution states propagate throughout the network. The store and collect
9// operations are implemented using one sided MPI communication methods
10// (simulating shared memory).
11// A revision number is used to prevent receiving previously collected
12// solution states from neighbors.
13//
14// Copyright (c) 2010 - 2013, Yves Ineichen, ETH Zürich
15// All rights reserved
16//
17// Implemented as part of the PhD thesis
18// "Toward massively parallel multi-objective optimization with application to
19// particle accelerators" (https://doi.org/10.3929/ethz-a-009792359)
20//
21// This file is part of OPAL.
22//
23// OPAL is free software: you can redistribute it and/or modify
24// it under the terms of the GNU General Public License as published by
25// the Free Software Foundation, either version 3 of the License, or
26// (at your option) any later version.
27//
28// You should have received a copy of the GNU General Public License
29// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
30//
31#ifndef __MASTER_NODE__
32#define __MASTER_NODE__
33
34#include <cmath>
35#include <set>
36#include <string>
37#include <sstream>
38#include <iostream>
39#include <vector>
40
41#include "mpi.h"
42
44
45
46//XXX: SolutionState_t must be serializable, i.e. its value_type must provide
47// writeState(std::ostream&) const and readState(std::istream&)!
48// (call SerializableSolutionState_t?)
49template <
50 class SolutionState_t
51 , class NeighborStrategy_t
52>
53class MasterNode : public NeighborStrategy_t {
54
61 struct Meta_t {
62 std::size_t revision = 0;
63 std::size_t size = 0;
64 };
65
66public:
67 MasterNode(MPI_Comm master_comm, std::size_t buf_size_upper_bound, std::size_t dim,
68 int island_id)
69 : buf_size_upper_bound_(buf_size_upper_bound)
70 , win_bytes_(sizeof(Meta_t) + buf_size_upper_bound)
71 , master_comm_(master_comm)
72 , revision_(0) {
73
74 int tmp = 0;
75 MPI_Comm_rank(master_comm, &tmp);
76 myID_ = static_cast<std::size_t>(tmp);
77
78 MPI_Comm_size(master_comm, &tmp);
79 numMasters_ = static_cast<std::size_t>(tmp);
81
82 // better to use MPI-2 memory allocation methods
83 MPI_Alloc_mem(win_bytes_, MPI_INFO_NULL, &serialized_best_values_);
84
85 // zero-initialize the header so an unwritten window reads as revision 0
86 Meta_t empty;
87 std::memcpy(serialized_best_values_, &empty, sizeof(Meta_t));
88
89 // expose our shared memory holding header + best values as one window
90 MPI_Win_create(serialized_best_values_, win_bytes_,
91 sizeof(char), MPI_INFO_NULL, master_comm, &win_);
92
93 // execute neighbor strategy to learn which neighbors have to be
94 // updated with our solution state (and we collect from)
95 collectFrom_ = this->execute(numMasters_, dim, myID_, island_id);
96 }
97
99 MPI_Win_free(&win_);
100 MPI_Free_mem(serialized_best_values_);
101 }
102
104 void store(char *local_state, std::size_t buffer_size) {
105
106 if (buffer_size > buf_size_upper_bound_) {
107 std::cerr << "MasterNode::store(): serialized state (" << buffer_size
108 << " bytes) exceeds window capacity (" << buf_size_upper_bound_
109 << " bytes), dropping this update" << std::endl;
110 return;
111 }
112
113 Meta_t meta;
114 meta.revision = ++revision_;
115 meta.size = buffer_size;
116
117 // header and payload are written within the same epoch so a reader
118 // can never observe a new revision paired with stale/partial data
119 MPI_Win_fence(MPI_MODE_NOPUT, win_);
120 std::memcpy(serialized_best_values_, &meta, sizeof(Meta_t));
121 std::memcpy(serialized_best_values_ + sizeof(Meta_t), local_state, buffer_size);
122 MPI_Win_fence(MPI_MODE_NOPUT, win_);
123 }
124
126 void collect(std::ostringstream &states) {
127
128 char *buffer;
129 MPI_Alloc_mem(win_bytes_, MPI_INFO_NULL, &buffer);
130 SolutionState_t tmp_states;
131
132 for (std::size_t i=0; i < numMasters_; i++) {
133 // ignore all except for selected master PIDs
134 if (i == myID_) continue;
135 if (collectFrom_.count(i) == 0) continue;
136
137 // cheap pre-check: only the header, to avoid a full fetch when unchanged
138 Meta_t hint;
139 MPI_Get(&hint, sizeof(Meta_t), MPI_BYTE, i, 0, sizeof(Meta_t), MPI_BYTE, win_);
140 MPI_Win_fence(0, win_);
141
142 if(hint.revision <= revision_state_[i]) continue;
143
144 // fetch header + payload together so they can never be torn apart
145 MPI_Get(buffer, win_bytes_, MPI_BYTE, i, 0, win_bytes_, MPI_BYTE, win_);
146 MPI_Win_fence(0, win_);
147
148 Meta_t meta;
149 std::memcpy(&meta, buffer, sizeof(Meta_t));
150
151 // re-check with the value that is guaranteed consistent with the payload
152 if (meta.revision <= revision_state_[i]) continue;
153 revision_state_[i] = meta.revision;
154
155 // build the stream from an explicit length, buffer is not null-terminated
156 std::istringstream is(std::string(buffer + sizeof(Meta_t), meta.size));
157
158 //XXX: ugly that we have to know the SolutionState_t here
159 SolutionState_t state;
160 readSolutionState(is, state);
161 tmp_states.insert(tmp_states.end(), state.begin(), state.end());
162 }
163
164 writeSolutionState(states, tmp_states);
165
166 MPI_Free_mem(buffer);
167 }
168
169private:
172
176 std::size_t win_bytes_;
177 std::size_t numMasters_;
178 MPI_Comm master_comm_;
179
183 MPI_Win win_;
184
185 std::size_t myID_;
186
188 std::set<std::size_t> collectFrom_;
190 std::size_t revision_;
192 std::vector<std::size_t> revision_state_;
193};
194
195#endif
void writeSolutionState(std::ostream &os, const SolutionState_t &state)
void readSolutionState(std::istream &is, SolutionState_t &state)
void collect(std::ostringstream &states)
collect all best values from all other masters
Definition MasterNode.h:126
std::size_t buf_size_upper_bound_
upper bound on the serialized payload (excludes the header)
Definition MasterNode.h:174
char * serialized_best_values_
pointer to MPI window holding header (revision + size) and best solution state
Definition MasterNode.h:171
MPI_Win win_
Definition MasterNode.h:183
std::vector< std::size_t > revision_state_
revision numbers of my neighbors
Definition MasterNode.h:192
std::size_t numMasters_
Definition MasterNode.h:177
std::size_t revision_
my solution state revision number
Definition MasterNode.h:190
MPI_Comm master_comm_
Definition MasterNode.h:178
std::size_t myID_
Definition MasterNode.h:185
MasterNode(MPI_Comm master_comm, std::size_t buf_size_upper_bound, std::size_t dim, int island_id)
Definition MasterNode.h:67
std::set< std::size_t > collectFrom_
neighbors we collect solution states from
Definition MasterNode.h:188
std::size_t win_bytes_
total window size in bytes: sizeof(Meta_t) + buf_size_upper_bound_
Definition MasterNode.h:176
void store(char *local_state, std::size_t buffer_size)
store my best values
Definition MasterNode.h:104
std::size_t size
Definition MasterNode.h:63
std::size_t revision
Definition MasterNode.h:62