MADNESS 0.10.1
lbdeux.h
Go to the documentation of this file.
1/*
2 This file is part of MADNESS.
3
4 Copyright (C) 2007,2010 Oak Ridge National Laboratory
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 For more information please contact:
21
22 Robert J. Harrison
23 Oak Ridge National Laboratory
24 One Bethel Valley Road
25 P.O. Box 2008, MS-6367
26
27 email: harrisonrj@ornl.gov
28 tel: 865-241-3937
29 fax: 865-572-0680
30
31 $Id$
32*/
33#ifndef MADNESS_MRA_IBDEUX_H__INCLUDED
34#define MADNESS_MRA_IBDEUX_H__INCLUDED
35
37#include <cmath>
38#include <iomanip>
39#include <iostream>
40#include <limits>
41#include <map>
42#include <queue>
43#include <string>
46
47#include <madness/mra/key.h>
49
50/// \file mra/lbdeux.h
51/// \brief Implements (2nd generation) static load/data balancing for functions
52/// \ingroup function
53
54namespace madness {
55
56 template<typename T, std::size_t NDIM>
57 class FunctionNode;
58
59 template<typename T, std::size_t NDIM>
60 class Function;
61
62 template <std::size_t NDIM>
63 class LBDeuxPmap : public WorldDCPmapInterface< Key<NDIM> > {
64 typedef Key<NDIM> keyT;
65 typedef std::pair<keyT,ProcessID> pairT;
66 typedef std::map<keyT,ProcessID> mapT;
68 typedef typename mapT::const_iterator iteratorT;
69
70 public:
71 LBDeuxPmap(const std::vector<pairT>& v) {
72 for (unsigned int i=0; i<v.size(); ++i) {
73 map.insert(v[i]);
74 }
75 }
76
77 // If level 0 is not entered as a node this will
78 // be an infinite loop.
79 ProcessID owner(const keyT& key) const {
80 while (key.level() >= 0) {
81 iteratorT it = map.find(key);
82 if (it == map.end()) {
83 return owner(key.parent());
84 }
85 else {
86 return it->second;
87 }
88 }
89 madness::print("Mon Dieux!", key);
90 throw "LBDeuxPmap: lookup failed";
91 }
92
93 void print() const {
94 madness::print("LBDeuxPmap");
95 }
96 };
97
98
99
100 template <std::size_t NDIM>
102 static const int nchild = (1<<NDIM);
106
107 double child_cost[nchild]; // Removed volatile since all parallel updates will be under mutex from active message and no unprotected reads
108 double my_cost;
111
113
114 /// Computes index of child key in this node using last bit of translations
115 int index(const keyT& key) {
116 int ind = 0;
117 for (std::size_t d=0; d<NDIM; ++d) ind += ((key.translation()[d])&0x1) << d;
118 return ind;
119 }
120
121 public:
123 : my_cost(0.0), total_cost(0.0), gotkids(false) {
124 nsummed = 0;
125 for (int i=0; i<nchild; ++i)
126 child_cost[i] = 0.0;
127 }
128
130 my_cost(other.my_cost), total_cost(other.total_cost), gotkids(other.gotkids)
131 {
132 nsummed = other.nsummed;
133 for (int i=0; i<nchild; ++i)
134 child_cost[i] = other.child_cost[i];
135 }
136
138 for (int i=0; i<nchild; ++i)
139 child_cost[i] = other.child_cost[i];
140 my_cost = other.my_cost;
141 total_cost = other.total_cost;
142 gotkids = other.gotkids;
143 nsummed = other.nsummed;
144
145 return *this;
146 }
147
148 bool has_children() const {
149 return gotkids;
150 }
151
152 double get_total_cost() const {
153 return total_cost;
154 }
155
156 /// Accumulates cost into this node
157 void add(double cost, bool got_kids) {
158 total_cost = (my_cost += cost);
160 }
161
162 /// Accumulates cost up the tree from children
163 void sum(const treeT& tree, const keyT& child, double value) {
164 child_cost[index(child)] = value;
165 ++nsummed;
166 if (nsummed == nchild) {
167 for (int i=0; i<nchild; ++i) total_cost += child_cost[i];
168 if (child.level() > 1) {
169 keyT key = child.parent();
170 keyT parent = key.parent();
171 const_cast<treeT&>(tree).task(parent, &nodeT::sum, tree, key, double(total_cost));
172 }
173 }
174 }
175
176
177 /// Logically deletes this node by setting cost to -1
178
179 /// Cannot actually erase this node from the container since the send() handler
180 /// is holding an accessor to it.
181 void deleter(const treeT& tree, const keyT& key) {
182 total_cost = my_cost = -1.0;
183 if (has_children()) {
184 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
185 const keyT child = kit.key();
186 const_cast<treeT&>(tree).task(child, &nodeT::deleter, tree, child);
187 }
188 }
189 }
190
191 /// Descends tree deleting all except internal nodes and sub-tree parents
192 void partition(const treeT& tree, const keyT& key, double avg) {
193 if (has_children()) {
194 // Sort children in descending cost order
196 double vals[nchild];
197 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
198 const keyT child = kit.key();
199 int ind = index(child);
200 keys[ind] = child;
201 vals[ind] = child_cost[ind];
202 }
203 for (int i=0; i<nchild; ++i) {
204 for (int j=i+1; j<nchild; ++j) {
205 if (vals[i] < vals[j]) {
206 std::swap(vals[i],vals[j]);
207 std::swap(keys[i],keys[j]);
208 }
209 }
210 }
211
212 // Split off subtrees in decreasing cost order
213 for (int i=0; i<nchild; ++i) {
214 if (total_cost <= avg) {
215 const_cast<treeT&>(tree).task(keys[i], &nodeT::deleter, tree, keys[i]);
216 }
217 else {
218 total_cost -= vals[i];
219 const_cast<treeT&>(tree).task(keys[i], &nodeT::partition, tree, keys[i], avg);
220 }
221 }
222 }
223 }
224
225 template <typename Archive>
226 void serialize(Archive& ar) {
227 ar & archive::wrap_opaque(this,1);
228 }
229 };
230
231
232 template <std::size_t NDIM>
237 typedef typename treeT::iterator iteratorT;
241
242
243 template <typename T, typename costT>
244 struct add_op {
246 const costT& costfn;
248 void operator()(const keyT& key, const FunctionNode<T,NDIM>& node) const {
249 if (lb->tree.is_local(key))
250 lb->tree.send(key, &nodeT::add, costfn(key,node), node.has_children());
251 else
252 lb->tree.task(key, &nodeT::add, costfn(key,node), node.has_children());
253 }
254 };
255
256 /// Sums costs up the tree returning to everyone the total cost
257 double sum() {
258 world.gop.fence();
259 const_iteratorT end = tree.end();
260 for (const_iteratorT it=tree.begin(); it!=end; ++it) {
261 const keyT& key = it->first;
262 const nodeT& node = it->second;
263 if (!node.has_children() && key.level() > 0) {
264 tree.task(key.parent(), &nodeT::sum, tree, key, node.get_total_cost());
265 }
266 }
267 world.gop.fence();
268 double total;
269 keyT key0(0);
270 if (world.rank() == tree.owner(key0)) {
271 total = tree.find(key0).get()->second.get_total_cost();
272 }
274 world.gop.fence();
275
276 return total;
277 }
278
279 /// Used to sort results into descending order
280 static bool compare(const std::pair<keyT,double>& a, const std::pair<keyT,double>& b) {
281 return a.second < b.second;
282 }
283
284
285 public:
287 : world(world)
288 , tree(world, FunctionDefaults<NDIM>::get_pmap()) {
289 world.gop.fence();
290 };
291
292 /// Accumulates cost from a function
293 template <typename T, typename costT>
294 void add_tree(const Function<T,NDIM>& f, const costT& costfn, bool fence=false) {
295 const_cast<Function<T,NDIM>&>(f).unaryop_node(add_op<T,costT>(this,costfn), fence);
296 }
297
298 /// Printing for the curious
299 void print_tree(const keyT& key = keyT(0)) {
301 iteratorT it = futit.get();
302 if (it != tree.end()) {
303 for (int i=0; i<key.level(); ++i) std::cout << " ";
304 print(key, it->second.get_total_cost());
305
306 if (it->second.has_children()) {
307 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
308 print_tree(kit.key());
309 }
310 }
311 }
312 }
313
314 /// Per-rank cost bar chart under the given pmap.
315 ///
316 /// Walks the aggregator tree locally; each entry with total_cost >= 0
317 /// contributes its total_cost to bucket pmap->owner(key). Buckets are
318 /// summed across ranks and rank 0 prints a horizontal bar chart with
319 /// total/avg/max/avg/max/min ratios.
320 ///
321 /// Call BEFORE load_balance() with the old pmap to see the per-key
322 /// distribution that the partitioner is about to act on; call AFTER
323 /// with the returned pmap to see how the bin-packed subtrees fell out.
324 /// Both invocations sum to the same total work, so the bars are
325 /// directly comparable.
326 void print_cost_per_rank(const std::string& label,
327 const std::shared_ptr< WorldDCPmapInterface<keyT> >& pmap,
328 std::ostream& os = std::cout) {
329 const int nproc = world.size();
330 std::vector<double> cost(nproc, 0.0);
331
332 const_iteratorT end = tree.end();
333 for (const_iteratorT it = tree.begin(); it != end; ++it) {
334 double c = it->second.get_total_cost();
335 if (c < 0.0) continue;
336 cost[pmap->owner(it->first)] += c;
337 }
338 world.gop.sum(cost.data(), nproc);
339 world.gop.fence();
340 if (world.rank() != 0) return;
341
342 double total = 0.0;
343 double mx = 0.0;
344 double mn = std::numeric_limits<double>::max();
345 for (double c : cost) {
346 total += c;
347 if (c > mx) mx = c;
348 if (c < mn) mn = c;
349 }
350 const double avg = (nproc > 0) ? total / nproc : 0.0;
351 const int barwidth = 40;
352
353 std::ios::fmtflags saved_flags = os.flags();
354 std::streamsize saved_prec = os.precision();
355
356 os << "=== Load balance summary: " << label << " ===\n";
357 os << std::fixed << std::setprecision(2)
358 << " total=" << total
359 << " avg=" << avg
360 << " max/avg=" << (avg > 0.0 ? mx / avg : 0.0);
361 if (mn > 0.0) os << " max/min=" << mx / mn;
362 os << "\n";
363 for (int p = 0; p < nproc; ++p) {
364 int n = (mx > 0.0) ? static_cast<int>(std::lround(barwidth * cost[p] / mx)) : 0;
365 if (n < 0) n = 0;
366 if (n > barwidth) n = barwidth;
367 os << " rank " << std::setw(3) << p << " |"
368 << std::string(n, '#')
369 << std::string(barwidth - n, ' ')
370 << "| " << std::setw(10) << cost[p]
371 << " (" << (avg > 0.0 ? cost[p] / avg : 0.0) << "x avg)\n";
372 }
373 os << "================================" << std::endl;
374
375 os.flags(saved_flags);
376 os.precision(saved_prec);
377 }
378
379 struct CostPerProc {
380 double cost;
381 int proc;
382 CostPerProc() : cost(0.0), proc(0) {}
383 CostPerProc(double cost, int proc) : cost(cost), proc(proc) {}
384 bool operator<(const CostPerProc& other) const {
385 return cost > other.cost; // Want ascending order
386 }
387 };
388
389 /// Actually does the partitioning of the tree
390 std::shared_ptr< WorldDCPmapInterface<keyT> > load_balance(double fac = 1.0, bool printstuff=false) {
391 world.gop.fence();
392 // Compute full tree of costs
393 double avg = sum()/(world.size()*fac);
394 //if (world.rank() == 0) print_tree();
395 world.gop.fence();
396
397 // Create partitioning
398 keyT key0(0);
399 if (world.rank() == tree.owner(key0)) {
400 tree.send(key0, &nodeT::partition, tree, key0, avg*1.1);
401 }
402 world.gop.fence();
403
404 // Collect entire vector onto node0
405 std::vector< std::pair<keyT,double> > results;
406 const_iteratorT end = tree.end();
407 for (const_iteratorT it=tree.begin(); it!=end; ++it) {
408 if (it->second.get_total_cost() >= 0) {
409 results.push_back(std::make_pair(it->first,it->second.get_total_cost()));
410 }
411 }
412 results = world.gop.concat0(results, 128*1024*1024);
413 world.gop.fence();
414
415 std::vector< std::pair<keyT,ProcessID> > map;
416
417 if (world.rank() == 0) {
418
419 std::sort(results.begin(), results.end(), compare);
420 if (printstuff) {
421 print("THESE ARE THE INITIAL SUBTREES");
422 for (unsigned int i=0; i<results.size(); ++i) print(i,results[i]);
423 }
424
425 // Now use bin packing to cram the results together
426 map.reserve(results.size());
427
428 // Shove the first nproc entries directly into the queue
429 unsigned int nproc = world.size();
430 std::priority_queue<CostPerProc> costs;
431 for (unsigned int p=0; p<nproc && !results.empty(); ++p) {
432 const std::pair<keyT,double>& f = results.back();
433 costs.push(CostPerProc(f.second,p));
434 map.push_back(std::make_pair(f.first,p));
435 results.pop_back();
436 }
437
438 // Process the remainder using the sorting maintained by the priority queue
439 while (!results.empty()) {
440 const std::pair<keyT,double>& f = results.back();
441 CostPerProc top = costs.top();
442 costs.pop();
443 top.cost += f.second;
444 costs.push(top);
445 map.push_back(std::make_pair(f.first,top.proc));
446 results.pop_back();
447 }
448 if (printstuff) {
449 print("THIS IS THE MAP");
450 print(map);
451 print("THESE ARE THE COSTS PER PROCESSOR");
452 while (!costs.empty()) {
453 print(costs.top().proc,costs.top().cost);
454 costs.pop();
455 }
456 }
457 }
458
459 world.gop.fence();
461 world.gop.fence();
462
463 // Return the Procmap
464
465 return std::shared_ptr< WorldDCPmapInterface<keyT> >(new LBDeuxPmap<NDIM>(map));
466 }
467 };
468}
469
470
471#endif // MADNESS_MRA_IBDEUX_H__INCLUDED
472
Implements AtomicInt.
An integer with atomic set, get, read+increment, read+decrement, and decrement+test operations.
Definition atomicint.h:126
FunctionDefaults holds default paramaters as static class members.
Definition funcdefaults.h:100
FunctionNode holds the coefficients, etc., at each node of the 2^NDIM-tree.
Definition funcimpl.h:136
bool has_children() const
Returns true if this node has children.
Definition funcimpl.h:217
A multiresolution adaptive numerical function.
Definition mra.h:144
A future is a possibly yet unevaluated value.
Definition future.h:370
Iterates in lexical order thru all children of a key.
Definition key.h:548
Key is the index for a node of the 2^NDIM-tree.
Definition key.h:70
Level level() const
Definition key.h:169
Key parent(int generation=1) const
Returns the key of the parent.
Definition key.h:290
const Vector< Translation, NDIM > & translation() const
Definition key.h:174
Definition lbdeux.h:63
ProcessID owner(const keyT &key) const
Maps key to processor.
Definition lbdeux.h:79
void print() const
Definition lbdeux.h:93
Key< NDIM > keyT
Definition lbdeux.h:64
mapT map
Definition lbdeux.h:67
std::map< keyT, ProcessID > mapT
Definition lbdeux.h:66
mapT::const_iterator iteratorT
Definition lbdeux.h:68
LBDeuxPmap(const std::vector< pairT > &v)
Definition lbdeux.h:71
std::pair< keyT, ProcessID > pairT
Definition lbdeux.h:65
Definition lbdeux.h:101
void add(double cost, bool got_kids)
Accumulates cost into this node.
Definition lbdeux.h:157
double get_total_cost() const
Definition lbdeux.h:152
LBNodeDeux()
Definition lbdeux.h:122
void serialize(Archive &ar)
Definition lbdeux.h:226
Key< NDIM > keyT
Definition lbdeux.h:103
void partition(const treeT &tree, const keyT &key, double avg)
Descends tree deleting all except internal nodes and sub-tree parents.
Definition lbdeux.h:192
bool gotkids
Definition lbdeux.h:110
AtomicInt nsummed
Definition lbdeux.h:112
LBNodeDeux< NDIM > & operator=(const LBNodeDeux< NDIM > &other)
Definition lbdeux.h:137
double total_cost
Definition lbdeux.h:109
static const int nchild
Definition lbdeux.h:102
bool has_children() const
Definition lbdeux.h:148
WorldContainer< keyT, nodeT > treeT
Definition lbdeux.h:105
int index(const keyT &key)
Computes index of child key in this node using last bit of translations.
Definition lbdeux.h:115
LBNodeDeux(const LBNodeDeux< NDIM > &other)
Definition lbdeux.h:129
double my_cost
Definition lbdeux.h:108
double child_cost[nchild]
Definition lbdeux.h:107
LBNodeDeux< NDIM > nodeT
Definition lbdeux.h:104
void deleter(const treeT &tree, const keyT &key)
Logically deletes this node by setting cost to -1.
Definition lbdeux.h:181
void sum(const treeT &tree, const keyT &child, double value)
Accumulates cost up the tree from children.
Definition lbdeux.h:163
Definition lbdeux.h:233
Key< NDIM > keyT
Definition lbdeux.h:234
World & world
Definition lbdeux.h:239
treeT::iterator iteratorT
Definition lbdeux.h:237
treeT tree
Definition lbdeux.h:240
LBNodeDeux< NDIM > nodeT
Definition lbdeux.h:235
std::shared_ptr< WorldDCPmapInterface< keyT > > load_balance(double fac=1.0, bool printstuff=false)
Actually does the partitioning of the tree.
Definition lbdeux.h:390
void print_tree(const keyT &key=keyT(0))
Printing for the curious.
Definition lbdeux.h:299
treeT::const_iterator const_iteratorT
Definition lbdeux.h:238
double sum()
Sums costs up the tree returning to everyone the total cost.
Definition lbdeux.h:257
static bool compare(const std::pair< keyT, double > &a, const std::pair< keyT, double > &b)
Used to sort results into descending order.
Definition lbdeux.h:280
void add_tree(const Function< T, NDIM > &f, const costT &costfn, bool fence=false)
Accumulates cost from a function.
Definition lbdeux.h:294
WorldContainer< keyT, nodeT > treeT
Definition lbdeux.h:236
LoadBalanceDeux(World &world)
Definition lbdeux.h:286
void print_cost_per_rank(const std::string &label, const std::shared_ptr< WorldDCPmapInterface< keyT > > &pmap, std::ostream &os=std::cout)
Definition lbdeux.h:326
Iterator for distributed container wraps the local iterator.
Definition worlddc.h:386
Makes a distributed container with specified attributes.
Definition worlddc.h:1127
bool find(accessor &acc, const keyT &key)
Write access to LOCAL value by key. Returns true if found, false otherwise (always false for remote).
Definition worlddc.h:1274
iterator begin()
Returns an iterator to the beginning of the local data (no communication)
Definition worlddc.h:1357
ProcessID owner(const keyT &key) const
Returns processor that logically owns key (no communication)
Definition worlddc.h:1321
iterator end()
Returns an iterator past the end of the local data (no communication)
Definition worlddc.h:1371
Future< REMFUTURE(MEMFUN_RETURNT(memfunT))> task(const keyT &key, memfunT memfun, const TaskAttributes &attr=TaskAttributes())
Adds task "resultT memfun()" in process owning item (non-blocking comm if remote)
Definition worlddc.h:1713
bool is_local(const keyT &key) const
Returns true if the key maps to the local processor (no communication)
Definition worlddc.h:1328
Future< MEMFUN_RETURNT(memfunT)> send(const keyT &key, memfunT memfun)
Sends message "resultT memfun()" to item (non-blocking comm if remote)
Definition worlddc.h:1470
Interface to be provided by any process map.
Definition worlddc.h:122
void broadcast_serializable(objT &obj, ProcessID root)
Broadcast a serializable object.
Definition worldgop.h:774
void fence(bool debug=false)
Synchronizes all processes in communicator AND globally ensures no pending AM or tasks.
Definition worldgop.cc:176
void broadcast(void *buf, size_t nbyte, ProcessID root, bool dowork=true, Tag bcast_tag=-1)
Broadcasts bytes from process root while still processing AM & tasks.
Definition worldgop.cc:188
std::vector< T > concat0(const std::vector< T > &v, size_t bufsz=1024 *1024)
Concatenate an STL vector of serializable stuff onto node 0.
Definition worldgop.h:973
void sum(T *buf, size_t nelem)
Inplace global sum while still processing AM & tasks.
Definition worldgop.h:890
A parallel world class.
Definition world.h:134
ProcessID rank() const
Returns the process rank in this World (same as MPI_Comm_rank()).
Definition world.h:344
ProcessID size() const
Returns the number of processes in this World (same as MPI_Comm_size()).
Definition world.h:354
WorldGopInterface & gop
Global operations.
Definition world.h:216
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
Provides FunctionDefaults and utilities for coordinate transformation.
archive_array< unsigned char > wrap_opaque(const T *, unsigned int)
Factory function to wrap a pointer to contiguous data as an opaque (uchar) archive_array.
Definition archive.h:926
static const double v
Definition hatom_sf_dirac.cc:20
Multidimension Key for MRA tree and associated iterators.
Macros and tools pertaining to the configuration of MADNESS.
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
void print(const T &t, const Ts &... ts)
Print items to std::cout (items separated by spaces) and terminate with a new line.
Definition print.h:227
NDIM & f
Definition mra.h:2622
static XNonlinearSolver< std::vector< Function< T, NDIM > >, T, vector_function_allocator< T, NDIM > > nonlinear_vector_solver(World &world, const long nvec)
Definition nonlinsol.h:371
static const double b
Definition nonlinschro.cc:119
static const double d
Definition nonlinschro.cc:121
static const double a
Definition nonlinschro.cc:118
static const double c
Definition relops.cc:10
CostPerProc(double cost, int proc)
Definition lbdeux.h:383
int proc
Definition lbdeux.h:381
bool operator<(const CostPerProc &other) const
Definition lbdeux.h:384
double cost
Definition lbdeux.h:380
CostPerProc()
Definition lbdeux.h:382
Definition lbdeux.h:244
void operator()(const keyT &key, const FunctionNode< T, NDIM > &node) const
Definition lbdeux.h:248
const costT & costfn
Definition lbdeux.h:246
LoadBalanceDeux * lb
Definition lbdeux.h:245
add_op(LoadBalanceDeux *lb, const costT &costfn)
Definition lbdeux.h:247
int task(int i)
Definition test_runtime.cpp:4
constexpr std::size_t NDIM
Definition testgconv.cc:54
Implements WorldContainer.
int ProcessID
Used to clearly identify process number/rank.
Definition worldtypes.h:43