PageRenderTime 75ms CodeModel.GetById 19ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 1ms

/Src/Dependencies/Boost/boost/graph/parallel/distribution.hpp

http://hadesmem.googlecode.com/
C++ Header | 615 lines | 464 code | 109 blank | 42 comment | 22 complexity | 98cbf900385789aef940cf4ee892c0fa MD5 | raw file
  1// Copyright 2004 The Trustees of Indiana University.
  2
  3// Use, modification and distribution is subject to the Boost Software
  4// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  5// http://www.boost.org/LICENSE_1_0.txt)
  6
  7//  Authors: Douglas Gregor
  8//           Peter Gottschling
  9//           Andrew Lumsdaine
 10#ifndef BOOST_PARALLEL_DISTRIBUTION_HPP
 11#define BOOST_PARALLEL_DISTRIBUTION_HPP
 12
 13#ifndef BOOST_GRAPH_USE_MPI
 14#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
 15#endif
 16
 17#include <cstddef>
 18#include <vector>
 19#include <algorithm>
 20#include <numeric>
 21#include <boost/assert.hpp>
 22#include <boost/iterator/counting_iterator.hpp>
 23#include <boost/random/uniform_int.hpp>
 24#include <boost/shared_ptr.hpp>
 25#include <typeinfo>
 26
 27namespace boost { namespace parallel {
 28
 29template<typename ProcessGroup, typename SizeType = std::size_t>
 30class variant_distribution
 31{
 32public:
 33  typedef typename ProcessGroup::process_id_type process_id_type;
 34  typedef typename ProcessGroup::process_size_type process_size_type;
 35  typedef SizeType size_type;
 36
 37private:
 38  struct basic_distribution
 39  {
 40    virtual ~basic_distribution() {}
 41    virtual size_type block_size(process_id_type, size_type) const = 0;
 42    virtual process_id_type in_process(size_type) const = 0;
 43    virtual size_type local(size_type) const = 0;
 44    virtual size_type global(size_type) const = 0;
 45    virtual size_type global(process_id_type, size_type) const = 0;
 46    virtual void* address() = 0;
 47    virtual const void* address() const = 0;
 48    virtual const std::type_info& type() const = 0;
 49  };
 50
 51  template<typename Distribution>
 52  struct poly_distribution : public basic_distribution
 53  {
 54    explicit poly_distribution(const Distribution& distribution)
 55      : distribution_(distribution) { }
 56
 57    virtual size_type block_size(process_id_type id, size_type n) const
 58    { return distribution_.block_size(id, n); }
 59
 60    virtual process_id_type in_process(size_type i) const
 61    { return distribution_(i); }
 62
 63    virtual size_type local(size_type i) const
 64    { return distribution_.local(i); }
 65
 66    virtual size_type global(size_type n) const
 67    { return distribution_.global(n); }
 68
 69    virtual size_type global(process_id_type id, size_type n) const
 70    { return distribution_.global(id, n); }
 71
 72    virtual void* address() { return &distribution_; }
 73    virtual const void* address() const { return &distribution_; }
 74    virtual const std::type_info& type() const { return typeid(Distribution); }
 75
 76  private:
 77    Distribution distribution_;
 78  };
 79
 80public:
 81  variant_distribution() { }
 82
 83  template<typename Distribution>
 84  variant_distribution(const Distribution& distribution)
 85    : distribution_(new poly_distribution<Distribution>(distribution)) { }
 86
 87  size_type block_size(process_id_type id, size_type n) const
 88  { return distribution_->block_size(id, n); }
 89  
 90  process_id_type operator()(size_type i) const
 91  { return distribution_->in_process(i); }
 92  
 93  size_type local(size_type i) const
 94  { return distribution_->local(i); }
 95  
 96  size_type global(size_type n) const
 97  { return distribution_->global(n); }
 98
 99  size_type global(process_id_type id, size_type n) const
100  { return distribution_->global(id, n); }
101
102  operator bool() const { return distribution_; }
103
104  void clear() { distribution_.reset(); }
105
106  template<typename T>
107  T* as()
108  {
109    if (distribution_->type() == typeid(T))
110      return static_cast<T*>(distribution_->address());
111    else
112      return 0;
113  }
114
115  template<typename T>
116  const T* as() const
117  {
118    if (distribution_->type() == typeid(T))
119      return static_cast<T*>(distribution_->address());
120    else
121      return 0;
122  }
123
124private:
125  shared_ptr<basic_distribution> distribution_;
126};
127
128struct block
129{
130  template<typename LinearProcessGroup>
131  explicit block(const LinearProcessGroup& pg, std::size_t n) 
132    : id(process_id(pg)), p(num_processes(pg)), n(n) { }
133
134  // If there are n elements in the distributed data structure, returns the number of elements stored locally.
135  template<typename SizeType>
136  SizeType block_size(SizeType n) const
137  { return (n / p) + ((std::size_t)(n % p) > id? 1 : 0); }
138
139  // If there are n elements in the distributed data structure, returns the number of elements stored on processor ID
140  template<typename SizeType, typename ProcessID>
141  SizeType block_size(ProcessID id, SizeType n) const
142  { return (n / p) + ((ProcessID)(n % p) > id? 1 : 0); }
143
144  // Returns the processor on which element with global index i is stored
145  template<typename SizeType>
146  SizeType operator()(SizeType i) const
147  { 
148    SizeType cutoff_processor = n % p;
149    SizeType cutoff = cutoff_processor * (n / p + 1);
150
151    if (i < cutoff) return i / (n / p + 1);
152    else return cutoff_processor + (i - cutoff) / (n / p);
153  }
154
155  // Find the starting index for processor with the given id
156  template<typename ID>
157  std::size_t start(ID id) const
158  {
159    std::size_t estimate = id * (n / p + 1);
160    ID cutoff_processor = n % p;
161    if (id < cutoff_processor) return estimate;
162    else return estimate - (id - cutoff_processor);
163  }
164
165  // Find the local index for the ith global element
166  template<typename SizeType>
167  SizeType local(SizeType i) const
168  { 
169    SizeType owner = (*this)(i);
170    return i - start(owner);
171  }
172
173  // Returns the global index of local element i
174  template<typename SizeType>
175  SizeType global(SizeType i) const
176  { return global(id, i); }
177
178  // Returns the global index of the ith local element on processor id
179  template<typename ProcessID, typename SizeType>
180  SizeType global(ProcessID id, SizeType i) const
181  { return i + start(id); }
182
183 private:
184  std::size_t id; //< The ID number of this processor
185  std::size_t p;  //< The number of processors
186  std::size_t n;  //< The size of the problem space
187};
188
189// Block distribution with arbitrary block sizes
190struct uneven_block
191{
192  typedef std::vector<std::size_t>    size_vector;
193
194  template<typename LinearProcessGroup>
195  explicit uneven_block(const LinearProcessGroup& pg, const std::vector<std::size_t>& local_sizes) 
196    : id(process_id(pg)), p(num_processes(pg)), local_sizes(local_sizes)
197  { 
198    BOOST_ASSERT(local_sizes.size() == p);
199    local_starts.resize(p + 1);
200    local_starts[0] = 0;
201    std::partial_sum(local_sizes.begin(), local_sizes.end(), &local_starts[1]);
202    n = local_starts[p];
203  }
204
205  // To do maybe: enter local size in each process and gather in constructor (much handier)
206  // template<typename LinearProcessGroup>
207  // explicit uneven_block(const LinearProcessGroup& pg, std::size_t my_local_size) 
208
209  // If there are n elements in the distributed data structure, returns the number of elements stored locally.
210  template<typename SizeType>
211  SizeType block_size(SizeType) const
212  { return local_sizes[id]; }
213
214  // If there are n elements in the distributed data structure, returns the number of elements stored on processor ID
215  template<typename SizeType, typename ProcessID>
216  SizeType block_size(ProcessID id, SizeType) const
217  { return local_sizes[id]; }
218
219  // Returns the processor on which element with global index i is stored
220  template<typename SizeType>
221  SizeType operator()(SizeType i) const
222  {
223    BOOST_ASSERT (i >= (SizeType) 0 && i < (SizeType) n); // check for valid range
224    size_vector::const_iterator lb = std::lower_bound(local_starts.begin(), local_starts.end(), (std::size_t) i);
225    return ((SizeType)(*lb) == i ? lb : --lb) - local_starts.begin();
226  }
227
228  // Find the starting index for processor with the given id
229  template<typename ID>
230  std::size_t start(ID id) const 
231  {
232    return local_starts[id];
233  }
234
235  // Find the local index for the ith global element
236  template<typename SizeType>
237  SizeType local(SizeType i) const
238  { 
239    SizeType owner = (*this)(i);
240    return i - start(owner);
241  }
242
243  // Returns the global index of local element i
244  template<typename SizeType>
245  SizeType global(SizeType i) const
246  { return global(id, i); }
247
248  // Returns the global index of the ith local element on processor id
249  template<typename ProcessID, typename SizeType>
250  SizeType global(ProcessID id, SizeType i) const
251  { return i + start(id); }
252
253 private:
254  std::size_t              id;           //< The ID number of this processor
255  std::size_t              p;            //< The number of processors
256  std::size_t              n;            //< The size of the problem space
257  std::vector<std::size_t> local_sizes;  //< The sizes of all blocks
258  std::vector<std::size_t> local_starts; //< Lowest global index of each block
259};
260
261
262struct oned_block_cyclic
263{
264  template<typename LinearProcessGroup>
265  explicit oned_block_cyclic(const LinearProcessGroup& pg, std::size_t size)
266    : id(process_id(pg)), p(num_processes(pg)), size(size) { }
267      
268  template<typename SizeType>
269  SizeType block_size(SizeType n) const
270  { 
271    return block_size(id, n);
272  }
273
274  template<typename SizeType, typename ProcessID>
275  SizeType block_size(ProcessID id, SizeType n) const
276  {
277    SizeType all_blocks = n / size;
278    SizeType extra_elements = n % size;
279    SizeType everyone_gets = all_blocks / p;
280    SizeType extra_blocks = all_blocks % p;
281    SizeType my_blocks = everyone_gets + (p < extra_blocks? 1 : 0);
282    SizeType my_elements = my_blocks * size 
283                         + (p == extra_blocks? extra_elements : 0);
284    return my_elements;
285  }
286
287  template<typename SizeType>
288  SizeType operator()(SizeType i) const
289  { 
290    return (i / size) % p;
291  }
292
293  template<typename SizeType>
294  SizeType local(SizeType i) const
295  { 
296    return ((i / size) / p) * size + i % size;
297  }
298
299  template<typename SizeType>
300  SizeType global(SizeType i) const
301  { return global(id, i); }
302
303  template<typename ProcessID, typename SizeType>
304  SizeType global(ProcessID id, SizeType i) const
305  { 
306    return ((i / size) * p + id) * size + i % size;
307  }
308
309 private:
310  std::size_t id;                   //< The ID number of this processor
311  std::size_t p;                    //< The number of processors
312  std::size_t size;                 //< Block size
313};
314
315struct twod_block_cyclic
316{
317  template<typename LinearProcessGroup>
318  explicit twod_block_cyclic(const LinearProcessGroup& pg,
319                             std::size_t block_rows, std::size_t block_columns,
320                             std::size_t data_columns_per_row)
321    : id(process_id(pg)), p(num_processes(pg)), 
322      block_rows(block_rows), block_columns(block_columns), 
323      data_columns_per_row(data_columns_per_row)
324  { }
325      
326  template<typename SizeType>
327  SizeType block_size(SizeType n) const
328  { 
329    return block_size(id, n);
330  }
331
332  template<typename SizeType, typename ProcessID>
333  SizeType block_size(ProcessID id, SizeType n) const
334  {
335    // TBD: This is really lame :)
336    int result = -1;
337    while (n > 0) {
338      --n;
339      if ((*this)(n) == id && (int)local(n) > result) result = local(n);
340    }
341    ++result;
342
343    //    std::cerr << "Block size of id " << id << " is " << result << std::endl;
344    return result;
345  }
346
347  template<typename SizeType>
348  SizeType operator()(SizeType i) const
349  { 
350    SizeType result = get_block_num(i) % p;
351    //    std::cerr << "Item " << i << " goes on processor " << result << std::endl;
352    return result;
353  }
354
355  template<typename SizeType>
356  SizeType local(SizeType i) const
357  { 
358    // Compute the start of the block
359    std::size_t block_num = get_block_num(i);
360    //    std::cerr << "Item " << i << " is in block #" << block_num << std::endl;
361
362    std::size_t local_block_num = block_num / p;
363    std::size_t block_start = local_block_num * block_rows * block_columns;
364
365    // Compute the offset into the block 
366    std::size_t data_row = i / data_columns_per_row;
367    std::size_t data_col = i % data_columns_per_row;
368    std::size_t block_offset = (data_row % block_rows) * block_columns 
369                             + (data_col % block_columns);    
370
371    //    std::cerr << "Item " << i << " maps to local index " << block_start+block_offset << std::endl;
372    return block_start + block_offset;
373  }
374
375  template<typename SizeType>
376  SizeType global(SizeType i) const
377  { 
378    // Compute the (global) block in which this element resides
379    SizeType local_block_num = i / (block_rows * block_columns);
380    SizeType block_offset = i % (block_rows * block_columns);
381    SizeType block_num = local_block_num * p + id;
382
383    // Compute the position of the start of the block (globally)
384    SizeType block_start = block_num * block_rows * block_columns;
385
386    std::cerr << "Block " << block_num << " starts at index " << block_start
387              << std::endl;
388
389    // Compute the row and column of this block
390    SizeType block_row = block_num / (data_columns_per_row / block_columns);
391    SizeType block_col = block_num % (data_columns_per_row / block_columns);
392
393    SizeType row_in_block = block_offset / block_columns;
394    SizeType col_in_block = block_offset % block_columns;
395
396    std::cerr << "Local index " << i << " is in block at row " << block_row
397              << ", column " << block_col << ", in-block row " << row_in_block
398              << ", in-block col " << col_in_block << std::endl;
399
400    SizeType result = block_row * block_rows + block_col * block_columns
401                    + row_in_block * block_rows + col_in_block;
402
403
404    std::cerr << "global(" << i << "@" << id << ") = " << result 
405              << " =? " << local(result) << std::endl;
406    BOOST_ASSERT(i == local(result));
407    return result;
408  }
409
410 private:
411  template<typename SizeType>
412  std::size_t get_block_num(SizeType i) const
413  {
414    std::size_t data_row = i / data_columns_per_row;
415    std::size_t data_col = i % data_columns_per_row;
416    std::size_t block_row = data_row / block_rows;
417    std::size_t block_col = data_col / block_columns;
418    std::size_t blocks_in_row = data_columns_per_row / block_columns;
419    std::size_t block_num = block_col * blocks_in_row + block_row;
420    return block_num;
421  }
422
423  std::size_t id;                   //< The ID number of this processor
424  std::size_t p;                    //< The number of processors
425  std::size_t block_rows;           //< The # of rows in each block
426  std::size_t block_columns;        //< The # of columns in each block
427  std::size_t data_columns_per_row; //< The # of columns per row of data
428};
429
430class twod_random
431{
432  template<typename RandomNumberGen>
433  struct random_int
434  {
435    explicit random_int(RandomNumberGen& gen) : gen(gen) { }
436
437    template<typename T>
438    T operator()(T n) const
439    {
440      uniform_int<T> distrib(0, n-1);
441      return distrib(gen);
442    }
443
444  private:
445    RandomNumberGen& gen;
446  };
447  
448 public:
449  template<typename LinearProcessGroup, typename RandomNumberGen>
450  explicit twod_random(const LinearProcessGroup& pg,
451                       std::size_t block_rows, std::size_t block_columns,
452                       std::size_t data_columns_per_row,
453                       std::size_t n,
454                       RandomNumberGen& gen)
455    : id(process_id(pg)), p(num_processes(pg)), 
456      block_rows(block_rows), block_columns(block_columns), 
457      data_columns_per_row(data_columns_per_row),
458      global_to_local(n / (block_rows * block_columns))
459  { 
460    std::copy(make_counting_iterator(std::size_t(0)),
461              make_counting_iterator(global_to_local.size()),
462              global_to_local.begin());
463
464    random_int<RandomNumberGen> rand(gen);
465    std::random_shuffle(global_to_local.begin(), global_to_local.end(), rand);
466  }
467      
468  template<typename SizeType>
469  SizeType block_size(SizeType n) const
470  { 
471    return block_size(id, n);
472  }
473
474  template<typename SizeType, typename ProcessID>
475  SizeType block_size(ProcessID id, SizeType n) const
476  {
477    // TBD: This is really lame :)
478    int result = -1;
479    while (n > 0) {
480      --n;
481      if ((*this)(n) == id && (int)local(n) > result) result = local(n);
482    }
483    ++result;
484
485    //    std::cerr << "Block size of id " << id << " is " << result << std::endl;
486    return result;
487  }
488
489  template<typename SizeType>
490  SizeType operator()(SizeType i) const
491  { 
492    SizeType result = get_block_num(i) % p;
493    //    std::cerr << "Item " << i << " goes on processor " << result << std::endl;
494    return result;
495  }
496
497  template<typename SizeType>
498  SizeType local(SizeType i) const
499  { 
500    // Compute the start of the block
501    std::size_t block_num = get_block_num(i);
502    //    std::cerr << "Item " << i << " is in block #" << block_num << std::endl;
503
504    std::size_t local_block_num = block_num / p;
505    std::size_t block_start = local_block_num * block_rows * block_columns;
506
507    // Compute the offset into the block 
508    std::size_t data_row = i / data_columns_per_row;
509    std::size_t data_col = i % data_columns_per_row;
510    std::size_t block_offset = (data_row % block_rows) * block_columns 
511                             + (data_col % block_columns);    
512
513    //    std::cerr << "Item " << i << " maps to local index " << block_start+block_offset << std::endl;
514    return block_start + block_offset;
515  }
516
517 private:
518  template<typename SizeType>
519  std::size_t get_block_num(SizeType i) const
520  {
521    std::size_t data_row = i / data_columns_per_row;
522    std::size_t data_col = i % data_columns_per_row;
523    std::size_t block_row = data_row / block_rows;
524    std::size_t block_col = data_col / block_columns;
525    std::size_t blocks_in_row = data_columns_per_row / block_columns;
526    std::size_t block_num = block_col * blocks_in_row + block_row;
527    return global_to_local[block_num];
528  }
529
530  std::size_t id;                   //< The ID number of this processor
531  std::size_t p;                    //< The number of processors
532  std::size_t block_rows;           //< The # of rows in each block
533  std::size_t block_columns;        //< The # of columns in each block
534  std::size_t data_columns_per_row; //< The # of columns per row of data
535  std::vector<std::size_t> global_to_local;
536};
537
538class random_distribution
539{
540  template<typename RandomNumberGen>
541  struct random_int
542  {
543    explicit random_int(RandomNumberGen& gen) : gen(gen) { }
544
545    template<typename T>
546    T operator()(T n) const
547    {
548      uniform_int<T> distrib(0, n-1);
549      return distrib(gen);
550    }
551
552  private:
553    RandomNumberGen& gen;
554  };
555  
556 public:
557  template<typename LinearProcessGroup, typename RandomNumberGen>
558  random_distribution(const LinearProcessGroup& pg, RandomNumberGen& gen,
559                      std::size_t n)
560    : base(pg, n), local_to_global(n), global_to_local(n)
561  {
562    std::copy(make_counting_iterator(std::size_t(0)),
563              make_counting_iterator(n),
564              local_to_global.begin());
565
566    random_int<RandomNumberGen> rand(gen);
567    std::random_shuffle(local_to_global.begin(), local_to_global.end(), rand);
568                        
569
570    for (std::vector<std::size_t>::size_type i = 0; i < n; ++i)
571      global_to_local[local_to_global[i]] = i;
572  }
573
574  template<typename SizeType>
575  SizeType block_size(SizeType n) const
576  { return base.block_size(n); }
577
578  template<typename SizeType, typename ProcessID>
579  SizeType block_size(ProcessID id, SizeType n) const
580  { return base.block_size(id, n); }
581
582  template<typename SizeType>
583  SizeType operator()(SizeType i) const
584  {
585    return base(global_to_local[i]);
586  }
587
588  template<typename SizeType>
589  SizeType local(SizeType i) const
590  { 
591    return base.local(global_to_local[i]);
592  }
593
594  template<typename ProcessID, typename SizeType>
595  SizeType global(ProcessID p, SizeType i) const
596  { 
597    return local_to_global[base.global(p, i)];
598  }
599
600  template<typename SizeType>
601  SizeType global(SizeType i) const
602  { 
603    return local_to_global[base.global(i)];
604  }
605
606 private:
607  block base;
608  std::vector<std::size_t> local_to_global;
609  std::vector<std::size_t> global_to_local;
610};
611
612} } // end namespace boost::parallel
613
614#endif // BOOST_PARALLEL_DISTRIBUTION_HPP
615