/home/runner/work/charm/charm/src/ck-ldb/TreeLB.C
Line | Count | Source (jump to first uncovered line) |
1 | | // Author: Juan Galvez <jjgalvez@illinois.edu> |
2 | | |
3 | | #include "TreeBuilder.h" // TODO this can be deleted if we change it so that LBManager instantiates the builders |
4 | | #include "TreeLB.h" |
5 | | #include "TreeStrategyFactory.h" |
6 | | #include "spanningTree.h" |
7 | | #include <fstream> // TODO delete if json file is read from LBManager |
8 | | #include <sstream> |
9 | | #include "json.hpp" |
10 | | |
11 | | extern int quietModeRequested; |
12 | | |
13 | | static void lbinit() |
14 | 256 | { |
15 | 256 | const auto& names = TreeStrategy::LBNames; |
16 | 256 | std::ostringstream o; |
17 | 256 | for (const auto& name : names) |
18 | 1.79k | { |
19 | 1.79k | o << "\n\t" << name; |
20 | 1.79k | } |
21 | 256 | LBRegisterBalancer<TreeLB>( |
22 | 256 | "TreeLB", "Pluggable hierarchical LB with available strategies:" + o.str()); |
23 | 256 | } |
24 | | |
25 | | void TreeLB::Migrated(int waitBarrier) |
26 | 49.9k | { |
27 | 49.9k | objMovedIn(waitBarrier); |
28 | 49.9k | } |
29 | | |
30 | | void TreeLB::loadConfigFile(const CkLBOptions& opts) |
31 | 353 | { |
32 | 353 | config.clear(); |
33 | 353 | std::ifstream ifs(_lb_args.treeLBFile(), std::ifstream::in); |
34 | 353 | if (opts.hasLegacyName()) |
35 | 354 | { |
36 | | // support legacy mode, e.g. map "+GreedyLB" to PE_Root tree using Greedy |
37 | | // use 2-level tree |
38 | 354 | config["tree"] = "PE_Root"; |
39 | 354 | config["root"]["strategies"] = {opts.getLegacyName()}; |
40 | 354 | if (CkMyPe() == 0 && !quietModeRequested) |
41 | 108 | CkPrintf("[%d] TreeLB in LEGACY MODE support\n", CkMyPe()); |
42 | 354 | } |
43 | 18.4E | else if (ifs.good()) |
44 | 0 | { |
45 | 0 | config = json::parse(ifs, nullptr, false); |
46 | 0 | if (config.is_discarded()) |
47 | 0 | { |
48 | 0 | CkPrintf( |
49 | 0 | "Error reading TreeLB configuration file: %s.\n" |
50 | 0 | "Ensure your configuration file is valid JSON.\n", |
51 | 0 | _lb_args.treeLBFile()); |
52 | 0 | CkExit(1); |
53 | 0 | } |
54 | 0 | } |
55 | 18.4E | else |
56 | 18.4E | { |
57 | 18.4E | if (CkMyPe() == 0 && !quietModeRequested) |
58 | 0 | CkPrintf( |
59 | 0 | "[%d] No TreeLB configuration file found. Choosing a default configuration.\n", |
60 | 0 | CkMyPe()); |
61 | | // try to pick reasonable defaults |
62 | | // the problem with using a 2 or 3 level tree in large jobs is that the root's |
63 | | // strategy could take a long time to run, and also many strategies (but not all) |
64 | | // could move objects across the whole machine (which means potentially slow |
65 | | // migrations and could also hurt communication performance depending on the |
66 | | // strategy). |
67 | 18.4E | if (CmiNumPhysicalNodes() >= 128) |
68 | 0 | { |
69 | | // use 4-level tree |
70 | 0 | config["tree"] = "PE_Process_ProcessGroup_Root"; |
71 | 0 | config["root"]["pe"] = 0; |
72 | 0 | config["processgroup"]["num_groups"] = CmiNumPhysicalNodes() / 32; |
73 | 0 | config["root"]["step_freq"] = 10; |
74 | 0 | config["processgroup"]["step_freq"] = 5; |
75 | 0 | config["process"]["strategies"] = {"GreedyRefine"}; |
76 | 0 | config["processgroup"]["strategies"] = {"GreedyRefine"}; |
77 | 0 | config["processgroup"]["GreedyRefine"]["tolerance"] = 1.03; |
78 | 0 | } |
79 | 18.4E | else if (CmiNumNodes() > 1 && CmiNodeSize(0) > 1) |
80 | 0 | { |
81 | | // use 3-level tree |
82 | 0 | config["tree"] = "PE_Process_Root"; |
83 | 0 | config["root"]["pe"] = 0; |
84 | 0 | config["root"]["step_freq"] = 3; |
85 | 0 | config["root"]["strategies"] = {"GreedyRefine"}; |
86 | 0 | config["process"]["strategies"] = {"GreedyRefine"}; |
87 | 0 | } |
88 | 18.4E | else |
89 | 18.4E | { |
90 | | // use 2-level tree |
91 | 18.4E | config["tree"] = "PE_Root"; |
92 | 18.4E | config["root"]["pe"] = 0; |
93 | 18.4E | config["root"]["strategies"] = {"GreedyRefine"}; |
94 | 18.4E | } |
95 | 18.4E | } |
96 | 353 | ifs.close(); |
97 | 353 | } |
98 | | |
99 | | void TreeLB::init(const CkLBOptions& opts) |
100 | 351 | { |
101 | 351 | #if CMK_LBDB_ON |
102 | | |
103 | 351 | lbname = "TreeLB"; |
104 | | |
105 | 351 | if (_lb_args.syncResume()) barrier_after_lb = true; |
106 | | |
107 | | // create and turn on by default |
108 | 351 | startLbFnHdl = lbmgr->AddStartLBFn(this, &TreeLB::StartLB); |
109 | | |
110 | 351 | configure(config); |
111 | | |
112 | | // TODO this functionality needs to move to LBManager |
113 | 351 | if (_lb_args.statsOn()) |
114 | 339 | lbmgr->CollectStatsOn(); // collect load and (optionally) comm stats |
115 | | |
116 | 351 | if (opts.getSeqNo() > 0) |
117 | 0 | { |
118 | 0 | turnOff(); |
119 | 0 | } |
120 | | |
121 | 351 | #endif |
122 | 351 | } |
123 | | |
124 | | TreeLB::~TreeLB() |
125 | 0 | { |
126 | 0 | #if CMK_LBDB_ON |
127 | 0 | lbmgr = CProxy_LBManager(_lbmgr).ckLocalBranch(); |
128 | 0 | if (lbmgr) |
129 | 0 | { |
130 | 0 | lbmgr->RemoveStartLBFn(startLbFnHdl); |
131 | 0 | } |
132 | |
|
133 | 0 | for (auto l : logic) delete l; |
134 | 0 | for (auto l : comm_logic) delete l; |
135 | |
|
136 | 0 | #endif |
137 | 0 | } |
138 | | |
139 | | void TreeLB::configure(LBTreeBuilder& builder, json& config) |
140 | 356 | { |
141 | 356 | #if CMK_LBDB_ON |
142 | | |
143 | 356 | if (numLevels > 0 && CkMyPe() == 0 && !quietModeRequested) |
144 | 0 | { |
145 | 0 | CkPrintf("[%d] Reconfiguring TreeLB\n", CkMyPe()); |
146 | 0 | } |
147 | | |
148 | 356 | mcast_bfactor = builder.getProperty("mcast_bfactor", mcast_bfactor, config); |
149 | 356 | step_report_lb_times = |
150 | 356 | builder.getProperty("report_lb_times_at_step", step_report_lb_times, config); |
151 | | |
152 | 356 | if (numLevels > 0) |
153 | 0 | { |
154 | 0 | for (auto l : logic) delete l; |
155 | 0 | for (auto l : comm_logic) delete l; |
156 | 0 | } |
157 | 356 | numLevels = builder.build(logic, comm_parent, comm_children, comm_logic, config); |
158 | 356 | CkAssert(numLevels > 0 && logic.size() == numLevels && |
159 | 356 | comm_parent.size() == numLevels && comm_children.size() == numLevels && |
160 | 356 | comm_logic.size() == numLevels); |
161 | | |
162 | 356 | expected_incoming.resize(numLevels); |
163 | 356 | expected_outgoing.resize(numLevels); |
164 | 356 | load_sent.resize(numLevels); |
165 | 356 | load_received.resize(numLevels); |
166 | | // notify_after_transfer.resize(numLevels); |
167 | 356 | awaitingLB.resize(numLevels); |
168 | | |
169 | | // reset all values since this may be a re-configuration |
170 | 356 | std::fill(expected_incoming.begin(), expected_incoming.end(), 0); |
171 | 356 | std::fill(expected_outgoing.begin(), expected_outgoing.end(), 0); |
172 | 356 | std::fill(load_sent.begin(), load_sent.end(), 0); |
173 | 356 | std::fill(load_received.begin(), load_received.end(), 0); |
174 | | // std::fill(notify_after_transfer.begin(), notify_after_transfer.end(), -1); |
175 | 356 | std::fill(awaitingLB.begin(), awaitingLB.end(), false); |
176 | | |
177 | 356 | #endif |
178 | 356 | } |
179 | | |
180 | | void TreeLB::configure(json& config) |
181 | 356 | { |
182 | 356 | #if CMK_LBDB_ON |
183 | 356 | const std::string& tree_type = config["tree"]; |
184 | 356 | if (tree_type == "PE_Root") |
185 | 353 | { |
186 | | // builder = new PE_Root_Tree(); |
187 | 353 | auto tree = PE_Root_Tree(config); |
188 | 353 | configure(tree, config); |
189 | 353 | } |
190 | 3 | else if (tree_type == "PE_Process_Root") |
191 | 0 | { |
192 | 0 | auto tree = PE_Node_Root_Tree(config); |
193 | 0 | configure(tree, config); |
194 | 0 | } |
195 | 3 | else if (tree_type == "PE_Process_ProcessGroup_Root") |
196 | 0 | { |
197 | 0 | auto tree = PE_Node_NodeSet_Root_Tree(config); |
198 | 0 | configure(tree, config); |
199 | 0 | } |
200 | 3 | else |
201 | 3 | { |
202 | 3 | CkAbort("TreeLB: configured tree not recognized\n"); |
203 | 3 | } |
204 | 356 | #endif |
205 | 356 | } |
206 | | |
207 | | void TreeLB::pup(PUP::er& p) |
208 | 20 | { |
209 | 20 | std::string configString; |
210 | 20 | if (p.isPacking()) |
211 | 8 | { |
212 | 8 | configString = config.dump(); |
213 | 8 | } |
214 | 20 | p | configString; |
215 | 20 | if (p.isUnpacking()) |
216 | 11 | { |
217 | 11 | config = json::parse(configString); |
218 | 11 | init(CkLBOptions(seqno)); |
219 | 11 | } |
220 | 20 | } |
221 | | |
222 | | void TreeLB::InvokeLB() |
223 | 3.71k | { |
224 | 3.71k | #if CMK_LBDB_ON |
225 | | // NOTE: I'm assuming new LBManager will know when (and when not to) call AtSync |
226 | 3.71k | if (barrier_before_lb) |
227 | 0 | { |
228 | 0 | contribute(CkCallback(CkReductionTarget(TreeLB, ProcessAtSync), thisProxy)); |
229 | 0 | } |
230 | 3.71k | else |
231 | 3.71k | { |
232 | 3.71k | thisProxy[CkMyPe()].ProcessAtSync(); |
233 | 3.71k | } |
234 | 3.71k | #endif |
235 | 3.71k | } |
236 | | |
237 | | void TreeLB::ProcessAtSync() |
238 | 3.70k | { |
239 | 3.70k | #if CMK_LBDB_ON |
240 | 3.70k | startTime = CkWallTimer(); |
241 | 3.70k | if (CkMyPe() == 0 && _lb_args.debug() > 0) |
242 | 152 | { |
243 | 152 | CkPrintf("--------- Started LB step %d ---------\n", lbmgr->step()); |
244 | 152 | } |
245 | | // CmiAssert(CmiNodeAlive(CkMyPe())); // TODO move this logic to LBManager |
246 | 3.70k | int level = 0; // load balancing starts at the lowest level |
247 | 3.70k | CkAssert(numLevels > 0 && !awaitingLB[level]); |
248 | 3.70k | TreeLBMessage* stats = logic[level]->getStats(); |
249 | 3.70k | stats->level = level; |
250 | 3.70k | awaitingLB[level] = true; |
251 | 3.70k | sendStatsUp((CkMessage*)stats); |
252 | 3.70k | #endif |
253 | 3.70k | } |
254 | | |
255 | | // send stats up using the comm-tree for this level |
256 | | void TreeLB::sendStatsUp(CkMessage* msg) |
257 | 6.31k | { |
258 | 6.31k | TreeLBMessage* stats = (TreeLBMessage*)msg; |
259 | 6.31k | int level = stats->level; |
260 | 6.31k | int comm_parent_pe = comm_parent[level]; |
261 | | // fprintf(stderr, "[%d] TreeLB::sendStatsUp - received msg level=%d comm_parent=%d\n", |
262 | | // CkMyPe(), level, comm_parent_pe); |
263 | 6.31k | if (comm_parent_pe == -1) |
264 | 3.36k | { |
265 | | // I'm the root of this comm-tree (current destination of the stats) |
266 | 3.36k | receiveStats(stats, level); |
267 | 3.36k | } |
268 | 2.94k | else if (comm_children[level].size() == 0) |
269 | 2.22k | { |
270 | | // don't have children so don't have to aggregate any msgs |
271 | 2.22k | thisProxy[comm_parent_pe].sendStatsUp((CkMessage*)stats); |
272 | 2.22k | } |
273 | 719 | else |
274 | 719 | { |
275 | 719 | LevelLogic* logic = comm_logic[level]; |
276 | 719 | logic->depositStats(stats); |
277 | 719 | if (logic->numStatsReceived() == comm_children[level].size() + 1) |
278 | 356 | { |
279 | 356 | TreeLBMessage* newMsg = logic->mergeStats(); |
280 | 356 | newMsg->level = level; |
281 | 356 | thisProxy[comm_parent_pe].sendStatsUp((CkMessage*)newMsg); |
282 | 356 | } |
283 | 719 | } |
284 | 6.31k | } |
285 | | |
286 | | void TreeLB::receiveStats(TreeLBMessage* stats, int level) |
287 | 3.36k | { |
288 | 3.36k | level += 1; |
289 | 3.36k | awaitingLB[level] = true; |
290 | 3.36k | LevelLogic* l = logic[level]; |
291 | 3.36k | l->depositStats(stats); |
292 | 3.36k | size_t expected_msgs = comm_children[level - 1].size(); |
293 | 3.36k | if (logic[level - 1] != nullptr) expected_msgs += 1; // expect msg from myself too |
294 | 3.36k | if (l->numStatsReceived() == expected_msgs) |
295 | 1.10k | { |
296 | 1.10k | CkAssert(load_sent[level] == 0 && load_received[level] == 0); |
297 | 1.10k | if (level == numLevels - 1 || l->cutoff()) |
298 | 1.10k | { |
299 | | // cutoff can be adjusted dynamically, to prevent lb between upper-level domains. |
300 | | // can be used, for example, to only do within-node lb on some steps |
301 | 1.10k | loadBalanceSubtree(level); |
302 | 1.10k | } |
303 | 0 | else |
304 | 0 | { |
305 | 0 | TreeLBMessage* newMsg = l->mergeStats(); |
306 | 0 | newMsg->level = level; |
307 | 0 | sendStatsUp((CkMessage*)newMsg); |
308 | 0 | } |
309 | 1.10k | } |
310 | 3.36k | } |
311 | | |
312 | | void TreeLB::loadBalanceSubtree(int level) |
313 | 5.61k | { |
314 | 5.61k | if (!awaitingLB[level]) return; |
315 | 4.81k | awaitingLB[level] = false; |
316 | 4.81k | if (level == 0) return lb_done(); |
317 | | |
318 | | // CkPrintf("[%d] TreeLB::loadBalanceSubtree - level=%d\n", CkMyPe(), level); |
319 | | |
320 | | /// CkMessage *inter_subtree_migrations = nullptr; |
321 | 1.10k | IDM idm; |
322 | 1.10k | TreeLBMessage* decision = logic[level]->loadBalance(idm); |
323 | 1.10k | if (idm.size() > 0) |
324 | 0 | { |
325 | | // this can happen when final destinations of chares has been decided, |
326 | | // and chares from a subtree need to migrate to a PE in a different subtree |
327 | 0 | std::vector<int> idm_dests(1 + idm.size()); |
328 | 0 | int index = 0; |
329 | 0 | idm_dests[index++] = CkMyPe(); |
330 | 0 | for (auto& move : idm) |
331 | 0 | { |
332 | 0 | CkAssert(move.second.size() > 0); |
333 | 0 | idm_dests[index++] = move.first; |
334 | 0 | } |
335 | 0 | ST_RecursivePartition<std::vector<int>::iterator> tb(false, false); |
336 | 0 | int num_subtrees = |
337 | 0 | tb.buildSpanningTree(idm_dests.begin(), idm_dests.end(), mcast_bfactor); |
338 | 0 | for (int i = 0; i < num_subtrees; i++) |
339 | 0 | thisProxy[*tb.begin(i)].multicastIDM(idm, tb.subtreeSize(i), &(*tb.begin(i))); |
340 | 0 | } |
341 | | |
342 | | // send decision to next level |
343 | 1.10k | decision->level = level - 1; |
344 | 1.10k | sendDecisionDown((CkMessage*)decision); |
345 | 1.10k | } |
346 | | |
347 | | void TreeLB::multicastIDM(const IDM& mig_order, int num_pes, int* _pes) |
348 | 0 | { |
349 | | #if DEBUG__TREE_LB_L3 |
350 | | fprintf(stderr, "[%d] Received IDM\n", CkMyPe()); |
351 | | #endif |
352 | 0 | ST_RecursivePartition<int*> tb(false, false); |
353 | 0 | if (num_pes > 1) |
354 | 0 | { |
355 | 0 | int num_subtrees = tb.buildSpanningTree(_pes, _pes + num_pes, mcast_bfactor); |
356 | 0 | for (int i = 0; i < num_subtrees; i++) |
357 | 0 | thisProxy[*tb.begin(i)].multicastIDM(mig_order, tb.subtreeSize(i), tb.begin(i)); |
358 | 0 | } |
359 | 0 | migrateObjects(mig_order); |
360 | 0 | } |
361 | | |
362 | | void TreeLB::sendDecisionDown(CkMessage* msg) |
363 | 3.70k | { |
364 | 3.70k | TreeLBMessage* decision = (TreeLBMessage*)msg; |
365 | 3.70k | const int level = decision->level; |
366 | 3.70k | std::vector<int>& children = comm_children[level]; |
367 | 3.70k | if (children.empty()) |
368 | 2.24k | { |
369 | 2.24k | receiveDecision(decision, level); |
370 | 2.24k | } |
371 | 1.45k | else |
372 | 1.45k | { |
373 | | // comm logic is free to split (scatter) the message, or send same msg to every child, |
374 | | // etc. |
375 | 1.45k | CkAssert(comm_logic[level] != nullptr); |
376 | 1.45k | std::vector<TreeLBMessage*> decisions = |
377 | 1.45k | comm_logic[level]->splitDecision(decision, children); |
378 | 1.45k | CkAssert(decisions.size() == children.size() + 1); |
379 | 1.45k | if (logic[level] != nullptr) |
380 | 1.45k | { |
381 | 1.45k | receiveDecision(decisions[0], level); |
382 | 1.45k | } |
383 | 1 | else |
384 | 1 | { |
385 | 1 | delete decisions[0]; |
386 | 1 | } |
387 | 4.07k | for (int i = 0; i < children.size(); i++) |
388 | 2.61k | { |
389 | | // Necessary because in some cases every message in decisions is actually |
390 | | // the same message that we are reusing, so mark as unused |
391 | 2.61k | _SET_USED(UsrToEnv(decisions[i + 1]), 0); |
392 | 2.61k | thisProxy[children[i]].sendDecisionDown((CkMessage*)decisions[i + 1]); |
393 | 2.61k | } |
394 | 1.45k | } |
395 | 3.70k | } |
396 | | |
397 | | void TreeLB::receiveDecision(TreeLBMessage* decision, int level) |
398 | 3.70k | { |
399 | | // fprintf(stderr, "[%d] TreeLB::receiveDecision, level=%d\n", CkMyPe(), level); |
400 | | |
401 | | // incoming and outgoing are integers. logic objects determine and interpret these |
402 | | // values |
403 | 3.70k | int& incoming = expected_incoming[level]; |
404 | 3.70k | int& outgoing = expected_outgoing[level]; |
405 | 3.70k | logic[level]->processDecision(decision, incoming, outgoing); |
406 | | // fprintf(stderr, "[%d] level=%d incoming=%d outgoing=%d\n", CkMyPe(), level, incoming, |
407 | | // outgoing); |
408 | 3.70k | if (incoming == 0 && outgoing == 0) |
409 | 797 | { |
410 | | // no exchange with other subtrees, can do lb for my subtree now |
411 | 797 | loadBalanceSubtree(level); |
412 | 797 | } |
413 | 2.90k | else |
414 | 2.90k | { |
415 | | // awaiting load from other subtrees |
416 | 2.90k | if (outgoing > 0 && level > 0) |
417 | 0 | { |
418 | | // need to pass info on actual chares to other subtree(s) (they will need it sooner |
419 | | // or later since some of my chares will be moving there, but I'm not migrating |
420 | | // chares themselves yet, since their final destination is not yet known) at this |
421 | | // level I might not know about individual chares. if that's the case, I have to |
422 | | // delegate to lower levels to pass this info to the other subtree |
423 | 0 | transferLoadToken(decision, level); |
424 | 0 | } |
425 | | |
426 | | // if outgoing > 0 and we are in last level (0), concrete objects are moved to |
427 | | // concrete PEs. for chares whose destination is known, the logic object would have |
428 | | // made call to migrate them inside processDecision (above) NOTE: I might be moving |
429 | | // some objects to other subtrees, might need to wait to receive messages from those |
430 | | // subtrees telling me to which PEs to migrate them |
431 | 2.90k | } |
432 | | // fprintf(stderr, "[%d] TreeLB::receiveDecision, deleting decision msg\n", CkMyPe()); |
433 | 3.70k | delete (CkMessage*)decision; // FIXME this won't call the subclass' delete method |
434 | | // a check here is needed because PE might have been sending chares/tokens or |
435 | | // receiving them even before receiving the decision msg (with the final tally) |
436 | | // from the parent. So, exchange might have already completed |
437 | 3.70k | checkLoadExchanged(level); |
438 | 3.70k | } |
439 | | |
440 | | // order transfer of load token to dest |
441 | | void TreeLB::transferLoadToken(TreeLBMessage* transferMsg, int level) |
442 | 0 | { |
443 | | // TODO this is a simplified implementation where the first level where tokens are |
444 | | // needed needs to be able to generate the tokens (so, token requests cannot be |
445 | | // propagated down the tree). There is a more advanced implementation in charm4py |
446 | 0 | if (logic[level]->makesTokens()) |
447 | 0 | { |
448 | | // one token set goes to one destination |
449 | 0 | std::vector<TreeLBMessage*> token_sets; |
450 | 0 | std::vector<int> destinations; |
451 | 0 | load_sent[level] += logic[level]->getTokenSets(transferMsg, token_sets, destinations); |
452 | 0 | for (size_t i = 0; i < token_sets.size(); i++) |
453 | 0 | { |
454 | 0 | token_sets[i]->level = level; |
455 | 0 | thisProxy[destinations[i]].recvLoadTokens((CkMessage*)token_sets[i]); |
456 | 0 | } |
457 | 0 | } |
458 | 0 | else |
459 | 0 | { |
460 | | // implemented in python. see code |
461 | 0 | CkAbort("TreeLB::transferLoadToken - NOT IMPLEMENTED\n"); |
462 | 0 | } |
463 | 0 | } |
464 | | |
465 | | void TreeLB::recvLoadTokens(CkMessage* tokens) |
466 | 0 | { |
467 | 0 | TreeLBMessage* token_set = (TreeLBMessage*)tokens; |
468 | 0 | int level = token_set->level; |
469 | | #if DEBUG__TREE_LB_L3 |
470 | | fprintf(stderr, "[%d] Received load token, level=%d\n", CkMyPe(), level); |
471 | | #endif |
472 | 0 | int load = logic[level]->tokensReceived(token_set); |
473 | 0 | load_received[level] += load; |
474 | 0 | checkLoadExchanged(level); |
475 | 0 | } |
476 | | |
477 | | void TreeLB::objMovedIn(bool waitBarrier) |
478 | 49.9k | { |
479 | 49.9k | if (!waitBarrier) CkAbort("TreeLB future migrates not supported\n"); |
480 | | |
481 | | // fprintf(stderr, "[%d] TreeLB::objMovedIn\n", CkMyPe()); |
482 | | |
483 | 49.9k | int level = 0; |
484 | 49.9k | CkAssert(numLevels > 0 && awaitingLB[level]); |
485 | 49.9k | load_received[level] += 1; |
486 | 49.9k | checkLoadExchanged(level); |
487 | 49.9k | } |
488 | | |
489 | | void TreeLB::migrateObjects(const IDM& mig_order) |
490 | 0 | { |
491 | 0 | int level = 0; |
492 | 0 | int sent = logic[level]->migrateObjects(mig_order.at(CkMyPe())); |
493 | 0 | load_sent[level] += sent; |
494 | | #if DEBUG__TREE_LB_L2 |
495 | | fprintf(stderr, "[%d] Received IDM order, sent=%d\n", CkMyPe(), sent); |
496 | | #endif |
497 | 0 | checkLoadExchanged(level); |
498 | 0 | } |
499 | | |
500 | | void TreeLB::lb_done() |
501 | 3.70k | { |
502 | | // fprintf(stderr, "[%d] lb_done step %d lb_time=%f\n", CkMyPe(), lbmgr->step(), |
503 | | // CkWallTimer() - startTime); |
504 | | |
505 | | // TODO LBManager should do all of this, including global syncResume ****** |
506 | | // Currently, TreeLB does syncResume by setting barrier_after_lb=true |
507 | | |
508 | | // clear load stats |
509 | 3.70k | lbmgr->ClearLoads(); |
510 | | |
511 | 3.70k | if (CkMyPe() == 0 && _lb_args.debug() > 0) |
512 | 152 | { |
513 | 152 | CkPrintf("--------- Finished LB step %d ---------\n", lbmgr->step()); |
514 | 152 | } |
515 | | |
516 | | // Advance to next load balancer |
517 | 3.70k | if (!(_lb_args.metaLbOn() && _lb_args.metaLbModelDir() != nullptr)) |
518 | 3.69k | lbmgr->nextLoadbalancer(seqno); |
519 | | |
520 | | // Increment to next step |
521 | 3.70k | lbmgr->incStep(); |
522 | | |
523 | 3.70k | LBManager::Object() |
524 | 3.70k | ->MigrationDone(); // call registered callbacks. not sure what this is for |
525 | | |
526 | | // again, TreeLB shouldn't be doing these things. it should just notify LBManager |
527 | | // that it's done. LBManager should take care of the rest. |
528 | | // not sure why this has to be called as entry method. but if not, it seems like |
529 | | // the last object to migrate in is not resumed |
530 | 3.70k | if (barrier_after_lb) |
531 | 0 | { |
532 | 0 | contribute(CkCallback(CkReductionTarget(TreeLB, resumeClients), thisProxy)); |
533 | 0 | } |
534 | 3.70k | else |
535 | 3.70k | { |
536 | 3.70k | thisProxy[CkMyPe()].resumeClients(); |
537 | 3.70k | } |
538 | 3.70k | } |
539 | | |
540 | | void TreeLB::resumeClients() |
541 | 3.68k | { |
542 | 3.68k | double lb_time = CkWallTimer() - startTime; |
543 | 3.68k | if (CkMyPe() == 0 && _lb_args.debug() > 0) |
544 | 152 | CkPrintf("[%d] lb time = %f\n", CkMyPe(), lb_time); |
545 | 3.68k | int step = lbmgr->step() - 1; |
546 | | // fprintf(stderr, "[%d] step %d lb_time=%f\n", CkMyPe(), step, lb_time); |
547 | 3.68k | if (step_report_lb_times >= step) |
548 | 0 | { |
549 | 0 | lb_times.push_back(lb_time); |
550 | 0 | if (step == step_report_lb_times) |
551 | 0 | { |
552 | 0 | contribute(lb_times, CkReduction::sum_double, |
553 | 0 | CkCallback(CkReductionTarget(TreeLB, reportLbTime), thisProxy[0])); |
554 | 0 | lb_times.clear(); |
555 | 0 | } |
556 | 0 | } |
557 | 3.68k | lbmgr->ResumeClients(); |
558 | 3.68k | } |
559 | | |
560 | | void TreeLB::reportLbTime(double* times, int n) |
561 | 0 | { |
562 | 0 | fprintf(stderr, "lb times: "); |
563 | 0 | for (int i = 0; i < n; i++) |
564 | 0 | { |
565 | 0 | double avg_time = times[i] / CkNumPes(); |
566 | 0 | fprintf(stderr, "%f ", avg_time); |
567 | 0 | } |
568 | 0 | fprintf(stderr, "\n"); |
569 | 0 | } |
570 | | |
571 | | #include "TreeLB.def.h" |