distribution.hpp 20 KB

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