File indexing completed on 2025-01-18 09:37:20
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #ifndef BOOST_PARALLEL_DISTRIBUTION_HPP
0011 #define BOOST_PARALLEL_DISTRIBUTION_HPP
0012
0013 #ifndef BOOST_GRAPH_USE_MPI
0014 #error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
0015 #endif
0016
0017 #include <cstddef>
0018 #include <vector>
0019 #include <algorithm>
0020 #include <numeric>
0021 #include <boost/assert.hpp>
0022 #include <boost/iterator/counting_iterator.hpp>
0023 #include <boost/random/uniform_int.hpp>
0024 #include <boost/shared_ptr.hpp>
0025 #include <boost/config.hpp>
0026 #include <typeinfo>
0027
0028 namespace boost { namespace parallel {
0029
0030 template<typename ProcessGroup, typename SizeType = std::size_t>
0031 class variant_distribution
0032 {
0033 public:
0034 typedef typename ProcessGroup::process_id_type process_id_type;
0035 typedef typename ProcessGroup::process_size_type process_size_type;
0036 typedef SizeType size_type;
0037
0038 private:
0039 struct basic_distribution
0040 {
0041 virtual ~basic_distribution() {}
0042 virtual size_type block_size(process_id_type, size_type) const = 0;
0043 virtual process_id_type in_process(size_type) const = 0;
0044 virtual size_type local(size_type) const = 0;
0045 virtual size_type global(size_type) const = 0;
0046 virtual size_type global(process_id_type, size_type) const = 0;
0047 virtual void* address() = 0;
0048 virtual const void* address() const = 0;
0049 virtual const std::type_info& type() const = 0;
0050 };
0051
0052 template<typename Distribution>
0053 struct poly_distribution : public basic_distribution
0054 {
0055 explicit poly_distribution(const Distribution& distribution)
0056 : distribution_(distribution) { }
0057
0058 virtual size_type block_size(process_id_type id, size_type n) const
0059 { return distribution_.block_size(id, n); }
0060
0061 virtual process_id_type in_process(size_type i) const
0062 { return distribution_(i); }
0063
0064 virtual size_type local(size_type i) const
0065 { return distribution_.local(i); }
0066
0067 virtual size_type global(size_type n) const
0068 { return distribution_.global(n); }
0069
0070 virtual size_type global(process_id_type id, size_type n) const
0071 { return distribution_.global(id, n); }
0072
0073 virtual void* address() { return &distribution_; }
0074 virtual const void* address() const { return &distribution_; }
0075 virtual const std::type_info& type() const { return typeid(Distribution); }
0076
0077 private:
0078 Distribution distribution_;
0079 };
0080
0081 public:
0082 variant_distribution() { }
0083
0084 template<typename Distribution>
0085 variant_distribution(const Distribution& distribution)
0086 : distribution_(new poly_distribution<Distribution>(distribution)) { }
0087
0088 size_type block_size(process_id_type id, size_type n) const
0089 { return distribution_->block_size(id, n); }
0090
0091 process_id_type operator()(size_type i) const
0092 { return distribution_->in_process(i); }
0093
0094 size_type local(size_type i) const
0095 { return distribution_->local(i); }
0096
0097 size_type global(size_type n) const
0098 { return distribution_->global(n); }
0099
0100 size_type global(process_id_type id, size_type n) const
0101 { return distribution_->global(id, n); }
0102
0103 operator bool() const { return distribution_; }
0104
0105 void clear() { distribution_.reset(); }
0106
0107 template<typename T>
0108 T* as()
0109 {
0110 if (distribution_->type() == typeid(T))
0111 return static_cast<T*>(distribution_->address());
0112 else
0113 return 0;
0114 }
0115
0116 template<typename T>
0117 const T* as() const
0118 {
0119 if (distribution_->type() == typeid(T))
0120 return static_cast<T*>(distribution_->address());
0121 else
0122 return 0;
0123 }
0124
0125 private:
0126 shared_ptr<basic_distribution> distribution_;
0127 };
0128
0129 struct block
0130 {
0131 template<typename LinearProcessGroup>
0132 explicit block(const LinearProcessGroup& pg, std::size_t n)
0133 : id(process_id(pg)), p(num_processes(pg)), n(n) { }
0134
0135
0136 template<typename SizeType>
0137 SizeType block_size(SizeType n) const
0138 { return (n / p) + ((std::size_t)(n % p) > id? 1 : 0); }
0139
0140
0141 template<typename SizeType, typename ProcessID>
0142 SizeType block_size(ProcessID id, SizeType n) const
0143 { return (n / p) + ((ProcessID)(n % p) > id? 1 : 0); }
0144
0145
0146 template<typename SizeType>
0147 SizeType operator()(SizeType i) const
0148 {
0149 SizeType cutoff_processor = n % p;
0150 SizeType cutoff = cutoff_processor * (n / p + 1);
0151
0152 if (i < cutoff) return i / (n / p + 1);
0153 else return cutoff_processor + (i - cutoff) / (n / p);
0154 }
0155
0156
0157 template<typename ID>
0158 std::size_t start(ID id) const
0159 {
0160 std::size_t estimate = id * (n / p + 1);
0161 ID cutoff_processor = n % p;
0162 if (id < cutoff_processor) return estimate;
0163 else return estimate - (id - cutoff_processor);
0164 }
0165
0166
0167 template<typename SizeType>
0168 SizeType local(SizeType i) const
0169 {
0170 SizeType owner = (*this)(i);
0171 return i - start(owner);
0172 }
0173
0174
0175 template<typename SizeType>
0176 SizeType global(SizeType i) const
0177 { return global(id, i); }
0178
0179
0180 template<typename ProcessID, typename SizeType>
0181 SizeType global(ProcessID id, SizeType i) const
0182 { return i + start(id); }
0183
0184 private:
0185 std::size_t id;
0186 std::size_t p;
0187 std::size_t n;
0188 };
0189
0190
0191 struct uneven_block
0192 {
0193 typedef std::vector<std::size_t> size_vector;
0194
0195 template<typename LinearProcessGroup>
0196 explicit uneven_block(const LinearProcessGroup& pg, const std::vector<std::size_t>& local_sizes)
0197 : id(process_id(pg)), p(num_processes(pg)), local_sizes(local_sizes)
0198 {
0199 BOOST_ASSERT(local_sizes.size() == p);
0200 local_starts.resize(p + 1);
0201 local_starts[0] = 0;
0202 std::partial_sum(local_sizes.begin(), local_sizes.end(), &local_starts[1]);
0203 n = local_starts[p];
0204 }
0205
0206
0207
0208
0209
0210
0211 template<typename SizeType>
0212 SizeType block_size(SizeType) const
0213 { return local_sizes[id]; }
0214
0215
0216 template<typename SizeType, typename ProcessID>
0217 SizeType block_size(ProcessID id, SizeType) const
0218 { return local_sizes[id]; }
0219
0220
0221 template<typename SizeType>
0222 SizeType operator()(SizeType i) const
0223 {
0224 BOOST_ASSERT (i >= (SizeType) 0 && i < (SizeType) n);
0225 size_vector::const_iterator lb = std::lower_bound(local_starts.begin(), local_starts.end(), (std::size_t) i);
0226 return ((SizeType)(*lb) == i ? lb : --lb) - local_starts.begin();
0227 }
0228
0229
0230 template<typename ID>
0231 std::size_t start(ID id) const
0232 {
0233 return local_starts[id];
0234 }
0235
0236
0237 template<typename SizeType>
0238 SizeType local(SizeType i) const
0239 {
0240 SizeType owner = (*this)(i);
0241 return i - start(owner);
0242 }
0243
0244
0245 template<typename SizeType>
0246 SizeType global(SizeType i) const
0247 { return global(id, i); }
0248
0249
0250 template<typename ProcessID, typename SizeType>
0251 SizeType global(ProcessID id, SizeType i) const
0252 { return i + start(id); }
0253
0254 private:
0255 std::size_t id;
0256 std::size_t p;
0257 std::size_t n;
0258 std::vector<std::size_t> local_sizes;
0259 std::vector<std::size_t> local_starts;
0260 };
0261
0262
0263 struct oned_block_cyclic
0264 {
0265 template<typename LinearProcessGroup>
0266 explicit oned_block_cyclic(const LinearProcessGroup& pg, std::size_t size)
0267 : id(process_id(pg)), p(num_processes(pg)), size(size) { }
0268
0269 template<typename SizeType>
0270 SizeType block_size(SizeType n) const
0271 {
0272 return block_size(id, n);
0273 }
0274
0275 template<typename SizeType, typename ProcessID>
0276 SizeType block_size(ProcessID id, SizeType n) const
0277 {
0278 SizeType all_blocks = n / size;
0279 SizeType extra_elements = n % size;
0280 SizeType everyone_gets = all_blocks / p;
0281 SizeType extra_blocks = all_blocks % p;
0282 SizeType my_blocks = everyone_gets + (p < extra_blocks? 1 : 0);
0283 SizeType my_elements = my_blocks * size
0284 + (p == extra_blocks? extra_elements : 0);
0285 return my_elements;
0286 }
0287
0288 template<typename SizeType>
0289 SizeType operator()(SizeType i) const
0290 {
0291 return (i / size) % p;
0292 }
0293
0294 template<typename SizeType>
0295 SizeType local(SizeType i) const
0296 {
0297 return ((i / size) / p) * size + i % size;
0298 }
0299
0300 template<typename SizeType>
0301 SizeType global(SizeType i) const
0302 { return global(id, i); }
0303
0304 template<typename ProcessID, typename SizeType>
0305 SizeType global(ProcessID id, SizeType i) const
0306 {
0307 return ((i / size) * p + id) * size + i % size;
0308 }
0309
0310 private:
0311 std::size_t id;
0312 std::size_t p;
0313 std::size_t size;
0314 };
0315
0316 struct twod_block_cyclic
0317 {
0318 template<typename LinearProcessGroup>
0319 explicit twod_block_cyclic(const LinearProcessGroup& pg,
0320 std::size_t block_rows, std::size_t block_columns,
0321 std::size_t data_columns_per_row)
0322 : id(process_id(pg)), p(num_processes(pg)),
0323 block_rows(block_rows), block_columns(block_columns),
0324 data_columns_per_row(data_columns_per_row)
0325 { }
0326
0327 template<typename SizeType>
0328 SizeType block_size(SizeType n) const
0329 {
0330 return block_size(id, n);
0331 }
0332
0333 template<typename SizeType, typename ProcessID>
0334 SizeType block_size(ProcessID id, SizeType n) const
0335 {
0336
0337 int result = -1;
0338 while (n > 0) {
0339 --n;
0340 if ((*this)(n) == id && (int)local(n) > result) result = local(n);
0341 }
0342 ++result;
0343
0344
0345 return result;
0346 }
0347
0348 template<typename SizeType>
0349 SizeType operator()(SizeType i) const
0350 {
0351 SizeType result = get_block_num(i) % p;
0352
0353 return result;
0354 }
0355
0356 template<typename SizeType>
0357 SizeType local(SizeType i) const
0358 {
0359
0360 std::size_t block_num = get_block_num(i);
0361
0362
0363 std::size_t local_block_num = block_num / p;
0364 std::size_t block_start = local_block_num * block_rows * block_columns;
0365
0366
0367 std::size_t data_row = i / data_columns_per_row;
0368 std::size_t data_col = i % data_columns_per_row;
0369 std::size_t block_offset = (data_row % block_rows) * block_columns
0370 + (data_col % block_columns);
0371
0372
0373 return block_start + block_offset;
0374 }
0375
0376 template<typename SizeType>
0377 SizeType global(SizeType i) const
0378 {
0379
0380 SizeType local_block_num = i / (block_rows * block_columns);
0381 SizeType block_offset = i % (block_rows * block_columns);
0382 SizeType block_num = local_block_num * p + id;
0383
0384
0385 SizeType block_start = block_num * block_rows * block_columns;
0386
0387 std::cerr << "Block " << block_num << " starts at index " << block_start
0388 << std::endl;
0389
0390
0391 SizeType block_row = block_num / (data_columns_per_row / block_columns);
0392 SizeType block_col = block_num % (data_columns_per_row / block_columns);
0393
0394 SizeType row_in_block = block_offset / block_columns;
0395 SizeType col_in_block = block_offset % block_columns;
0396
0397 std::cerr << "Local index " << i << " is in block at row " << block_row
0398 << ", column " << block_col << ", in-block row " << row_in_block
0399 << ", in-block col " << col_in_block << std::endl;
0400
0401 SizeType result = block_row * block_rows + block_col * block_columns
0402 + row_in_block * block_rows + col_in_block;
0403
0404
0405 std::cerr << "global(" << i << "@" << id << ") = " << result
0406 << " =? " << local(result) << std::endl;
0407 BOOST_ASSERT(i == local(result));
0408 return result;
0409 }
0410
0411 private:
0412 template<typename SizeType>
0413 std::size_t get_block_num(SizeType i) const
0414 {
0415 std::size_t data_row = i / data_columns_per_row;
0416 std::size_t data_col = i % data_columns_per_row;
0417 std::size_t block_row = data_row / block_rows;
0418 std::size_t block_col = data_col / block_columns;
0419 std::size_t blocks_in_row = data_columns_per_row / block_columns;
0420 std::size_t block_num = block_col * blocks_in_row + block_row;
0421 return block_num;
0422 }
0423
0424 std::size_t id;
0425 std::size_t p;
0426 std::size_t block_rows;
0427 std::size_t block_columns;
0428 std::size_t data_columns_per_row;
0429 };
0430
0431 class twod_random
0432 {
0433 template<typename RandomNumberGen>
0434 struct random_int
0435 {
0436 explicit random_int(RandomNumberGen& gen) : gen(gen) { }
0437
0438 template<typename T>
0439 T operator()(T n) const
0440 {
0441 uniform_int<T> distrib(0, n-1);
0442 return distrib(gen);
0443 }
0444
0445 private:
0446 RandomNumberGen& gen;
0447 };
0448
0449 public:
0450 template<typename LinearProcessGroup, typename RandomNumberGen>
0451 explicit twod_random(const LinearProcessGroup& pg,
0452 std::size_t block_rows, std::size_t block_columns,
0453 std::size_t data_columns_per_row,
0454 std::size_t n,
0455 RandomNumberGen& gen)
0456 : id(process_id(pg)), p(num_processes(pg)),
0457 block_rows(block_rows), block_columns(block_columns),
0458 data_columns_per_row(data_columns_per_row),
0459 global_to_local(n / (block_rows * block_columns))
0460 {
0461 std::copy(make_counting_iterator(std::size_t(0)),
0462 make_counting_iterator(global_to_local.size()),
0463 global_to_local.begin());
0464
0465 #if defined(BOOST_NO_CXX98_RANDOM_SHUFFLE)
0466 std::shuffle(global_to_local.begin(), global_to_local.end(), gen);
0467 #else
0468 random_int<RandomNumberGen> rand(gen);
0469 std::random_shuffle(global_to_local.begin(), global_to_local.end(), rand);
0470 #endif
0471 }
0472
0473 template<typename SizeType>
0474 SizeType block_size(SizeType n) const
0475 {
0476 return block_size(id, n);
0477 }
0478
0479 template<typename SizeType, typename ProcessID>
0480 SizeType block_size(ProcessID id, SizeType n) const
0481 {
0482
0483 int result = -1;
0484 while (n > 0) {
0485 --n;
0486 if ((*this)(n) == id && (int)local(n) > result) result = local(n);
0487 }
0488 ++result;
0489
0490
0491 return result;
0492 }
0493
0494 template<typename SizeType>
0495 SizeType operator()(SizeType i) const
0496 {
0497 SizeType result = get_block_num(i) % p;
0498
0499 return result;
0500 }
0501
0502 template<typename SizeType>
0503 SizeType local(SizeType i) const
0504 {
0505
0506 std::size_t block_num = get_block_num(i);
0507
0508
0509 std::size_t local_block_num = block_num / p;
0510 std::size_t block_start = local_block_num * block_rows * block_columns;
0511
0512
0513 std::size_t data_row = i / data_columns_per_row;
0514 std::size_t data_col = i % data_columns_per_row;
0515 std::size_t block_offset = (data_row % block_rows) * block_columns
0516 + (data_col % block_columns);
0517
0518
0519 return block_start + block_offset;
0520 }
0521
0522 private:
0523 template<typename SizeType>
0524 std::size_t get_block_num(SizeType i) const
0525 {
0526 std::size_t data_row = i / data_columns_per_row;
0527 std::size_t data_col = i % data_columns_per_row;
0528 std::size_t block_row = data_row / block_rows;
0529 std::size_t block_col = data_col / block_columns;
0530 std::size_t blocks_in_row = data_columns_per_row / block_columns;
0531 std::size_t block_num = block_col * blocks_in_row + block_row;
0532 return global_to_local[block_num];
0533 }
0534
0535 std::size_t id;
0536 std::size_t p;
0537 std::size_t block_rows;
0538 std::size_t block_columns;
0539 std::size_t data_columns_per_row;
0540 std::vector<std::size_t> global_to_local;
0541 };
0542
0543 class random_distribution
0544 {
0545 template<typename RandomNumberGen>
0546 struct random_int
0547 {
0548 explicit random_int(RandomNumberGen& gen) : gen(gen) { }
0549
0550 template<typename T>
0551 T operator()(T n) const
0552 {
0553 uniform_int<T> distrib(0, n-1);
0554 return distrib(gen);
0555 }
0556
0557 private:
0558 RandomNumberGen& gen;
0559 };
0560
0561 public:
0562 template<typename LinearProcessGroup, typename RandomNumberGen>
0563 random_distribution(const LinearProcessGroup& pg, RandomNumberGen& gen,
0564 std::size_t n)
0565 : base(pg, n), local_to_global(n), global_to_local(n)
0566 {
0567 std::copy(make_counting_iterator(std::size_t(0)),
0568 make_counting_iterator(n),
0569 local_to_global.begin());
0570
0571 #if defined(BOOST_NO_CXX98_RANDOM_SHUFFLE)
0572 std::shuffle(local_to_global.begin(), local_to_global.end(), gen);
0573 #else
0574 random_int<RandomNumberGen> rand(gen);
0575 std::random_shuffle(local_to_global.begin(), local_to_global.end(), rand);
0576 #endif
0577
0578 for (std::vector<std::size_t>::size_type i = 0; i < n; ++i)
0579 global_to_local[local_to_global[i]] = i;
0580 }
0581
0582 template<typename SizeType>
0583 SizeType block_size(SizeType n) const
0584 { return base.block_size(n); }
0585
0586 template<typename SizeType, typename ProcessID>
0587 SizeType block_size(ProcessID id, SizeType n) const
0588 { return base.block_size(id, n); }
0589
0590 template<typename SizeType>
0591 SizeType operator()(SizeType i) const
0592 {
0593 return base(global_to_local[i]);
0594 }
0595
0596 template<typename SizeType>
0597 SizeType local(SizeType i) const
0598 {
0599 return base.local(global_to_local[i]);
0600 }
0601
0602 template<typename ProcessID, typename SizeType>
0603 SizeType global(ProcessID p, SizeType i) const
0604 {
0605 return local_to_global[base.global(p, i)];
0606 }
0607
0608 template<typename SizeType>
0609 SizeType global(SizeType i) const
0610 {
0611 return local_to_global[base.global(i)];
0612 }
0613
0614 private:
0615 block base;
0616 std::vector<std::size_t> local_to_global;
0617 std::vector<std::size_t> global_to_local;
0618 };
0619
0620 } }
0621
0622 #endif
0623