Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Tpetra_Distribution2D.hpp
1 // @HEADER
2 // *****************************************************************************
3 // Tpetra: Templated Linear Algebra Services Package
4 //
5 // Copyright 2008 NTESS and the Tpetra contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 // 2D matrix distribution
11 // Assumes square matrix
12 // Karen Devine, SNL
13 //
14 
15 #ifndef __TPETRA_DISTRIBUTION2D_HPP
16 #define __TPETRA_DISTRIBUTION2D_HPP
17 
18 namespace Tpetra {
19 
21 template <typename gno_t, typename scalar_t>
22 class Distribution2D : public Distribution<gno_t, scalar_t> {
23  // Processors will be laid out logically first down columns then
24  // across rows. For example, assume np = 8, npRows=2, npCols=4.
25  // Then the processors will be laid out in 2D as
26  // 0 2 4 6
27  // 1 3 5 7
28  //
29  // The matrix will be distributed using np=8 row blocks:
30  // 0 2 4 6
31  // 1 3 5 7
32  // 0 2 4 6
33  // 1 3 5 7
34  // 0 2 4 6
35  // 1 3 5 7
36  // 0 2 4 6
37  // 1 3 5 7
38  //
39  // The vector will be distributed linearly or randomly. The row and
40  // column maps will be built to allow only row- or column-based
41  // communication in the matvec.
42 
43  public:
44  using Distribution<gno_t, scalar_t>::me;
45  using Distribution<gno_t, scalar_t>::np;
46  using Distribution<gno_t, scalar_t>::comm;
47  using Distribution<gno_t, scalar_t>::nrows;
48  using Distribution<gno_t, scalar_t>::Mine;
49 
50  Distribution2D(size_t nrows_,
51  const Teuchos::RCP<const Teuchos::Comm<int> > &comm_,
52  const Teuchos::ParameterList &params)
53  : Distribution<gno_t, scalar_t>(nrows_, comm_, params)
54  , npRows(-1)
55  , npCols(-1) {
56  {
57  const Teuchos::ParameterEntry *pe = params.getEntryPtr("nProcessorRows");
58  if (pe != NULL)
59  npRows = pe->getValue<int>(&npRows);
60  }
61 
62  {
63  const Teuchos::ParameterEntry *pe = params.getEntryPtr("nProcessorCols");
64  if (pe != NULL)
65  npCols = pe->getValue<int>(&npCols);
66  }
67 
68  // Compute the processor configuration npRows * npCols
69 
70  if (npRows == -1 && npCols == -1) { // Compute both npRows and npCols
71  // First guess
72  npRows = (int)(sqrt(np));
73  npCols = np / npRows;
74  // Adjust npRows so that npRows * npCols == np
75  while (npRows * npCols != np) {
76  npRows++;
77  npCols = np / npRows;
78  }
79  } else { // User specified either npRows or npCols
80  if (npRows == -1) // npCols specified; compute npRows
81  npRows = np / npCols;
82  else if (npCols == -1) // npRows specified; compute npCols
83  npCols = np / npRows;
84 
85  if (npCols * npRows != np) {
86  TEUCHOS_TEST_FOR_EXCEPTION(npRows * npCols != np, std::logic_error,
87  "nProcessorCols " << npCols << " * nProcessorRows " << npRows << " = " << npCols * npRows << " must equal nProcessors " << np << " for 2D distribution");
88  }
89  }
90  if (me == 0)
91  std::cout << "\n2D Distribution: "
92  << "\n npRows = " << npRows
93  << "\n npCols = " << npCols
94  << "\n np = " << np << std::endl;
95 
96  mypCol = this->TWODPCOL(me);
97  mypRow = this->TWODPROW(me);
98  }
99 
100  virtual ~Distribution2D(){};
101 
102  protected:
103  // Return the processor row for rank p
104  inline int TWODPROW(int p) { return (p % npRows); }
105 
106  // Return the processor column for rank p
107  inline int TWODPCOL(int p) { return (p / npRows); }
108 
109  // Return the rank for processor row i and processor column j
110  inline int TWODPRANK(int i, int j) { return (j * npRows + (j + i) % npRows); }
111 
112  int npRows; // Number of processor rows
113  int npCols; // Number of processor columns
114  int mypRow; // This rank's processor row
115  int mypCol; // This rank's processor column
116 };
117 
119 template <typename gno_t, typename scalar_t>
120 class Distribution2DLinear : public Distribution2D<gno_t, scalar_t> {
121  public:
122  using Distribution<gno_t, scalar_t>::me;
123  using Distribution<gno_t, scalar_t>::np;
124  using Distribution<gno_t, scalar_t>::nrows;
125  using Distribution2D<gno_t, scalar_t>::npRows;
126  using Distribution2D<gno_t, scalar_t>::npCols;
127  using Distribution2D<gno_t, scalar_t>::mypRow;
128  using Distribution2D<gno_t, scalar_t>::mypCol;
129 
130  Distribution2DLinear(size_t nrows_,
131  const Teuchos::RCP<const Teuchos::Comm<int> > &comm_,
132  const Teuchos::ParameterList &params)
133  : Distribution2D<gno_t, scalar_t>(nrows_, comm_, params) {
134  // Build vector describing distribution of vector entries across ranks
135  entries.assign(np + 1, nrows / np);
136  int nExtraEntries = nrows % np;
137 
138  // Distribute the extra entries evenly among processors.
139  // To evenly distribute them extra entries among processor rows and
140  // columns, we distribute them along diagonals of the matrix distribution.
141  // For our example, assume we have seven extra values (the max possible
142  // with np=8). Then we give one extra entry each to ranks
143  // [0, 3, 4, 7, 1, 2, 5]. For fewer extra entries, we follow the same
144  // order of assignment, and just stop early.
145 
146  for (int cnt = 0, i = 0; (cnt < nExtraEntries) && (i < npRows); i++) {
147  for (int j = 0; (cnt < nExtraEntries) && (j < npCols); cnt++, j++) {
148  int rankForExtra = Distribution2D<gno_t, scalar_t>::TWODPRANK(i, j);
149  entries[rankForExtra + 1]++; // Store in rankForExtra+1 to simplify
150  // prefix sum.
151  }
152  }
153 
154  // Perform prefix sum of entries.
155  entries[0] = 0;
156  for (int i = 1; i <= np; i++)
157  entries[i] = entries[i - 1] + entries[i];
158  // Now entries contains the first vector entry for each rank.
159 
160  // Column map: Easy; consecutive entries for all ranks in column.
161  int firstRank = mypCol * npRows; // First rank in my column
162  myFirstCol = entries[firstRank];
163 
164  gno_t nMyCols = 0;
165  for (int i = firstRank; i < firstRank + npRows; i++)
166  nMyCols += entries[i + 1] - entries[i];
167  myLastCol = myFirstCol + nMyCols - 1;
168  }
169 
170  inline enum DistributionType DistType() { return TwoDLinear; }
171 
172  bool Mine(gno_t i, gno_t j) {
173  int idx = int(float(i) * float(np) / float(nrows));
174  while (i < entries[idx]) idx--;
175  while (i >= entries[idx + 1]) idx++;
176  return ((mypRow == Distribution2D<gno_t, scalar_t>::TWODPROW(idx)) // RowMine
177  && (j >= myFirstCol && j <= myLastCol)); // ColMine
178  }
179  inline bool Mine(gno_t i, gno_t j, int p) { return Mine(i, j); }
180 
181  inline bool VecMine(gno_t i) {
182  return (i >= entries[me] && i < entries[me + 1]);
183  }
184 
185  private:
186  std::vector<gno_t> entries; // Describes vector entries' distribution to ranks
187  // Organized like vtxdist
188  gno_t myFirstCol; // First column owned by this rank
189  gno_t myLastCol; // Last column owned by this rank
190 };
191 
193 template <typename gno_t, typename scalar_t>
194 class Distribution2DRandom : public Distribution2D<gno_t, scalar_t> {
195  public:
196  using Distribution<gno_t, scalar_t>::me;
197  using Distribution2D<gno_t, scalar_t>::mypRow;
198  using Distribution2D<gno_t, scalar_t>::mypCol;
199 
200  Distribution2DRandom(size_t nrows_,
201  const Teuchos::RCP<const Teuchos::Comm<int> > &comm_,
202  const Teuchos::ParameterList &params)
203  : Distribution2D<gno_t, scalar_t>(nrows_, comm_, params) {
204  if (me == 0) std::cout << " randomize = true" << std::endl;
205  }
206 
207  inline enum DistributionType DistType() { return TwoDRandom; }
208 
209  inline bool Mine(gno_t i, gno_t j) {
210  return ((mypRow == this->TWODPROW(this->HashToProc(i))) && // RowMine
211  (mypCol == this->TWODPCOL(this->HashToProc(j)))); // ColMine
212  }
213  inline bool Mine(gno_t i, gno_t j, int p) { return Mine(i, j); }
214 
215  inline bool VecMine(gno_t i) { return (me == this->HashToProc(i)); }
216 };
217 
219 
220 template <typename gno_t, typename scalar_t>
221 class Distribution2DVec : public Distribution2D<gno_t, scalar_t> {
222  // Distribute non-zeros in a 2D manner based on the vector distribution
223  // and the nprows x npcols configuration;
224  // rows are assigned to same process owning the vector entry.
225  public:
226  using Distribution<gno_t, scalar_t>::me;
227  using Distribution<gno_t, scalar_t>::np;
228  using Distribution<gno_t, scalar_t>::comm;
229  using Distribution<gno_t, scalar_t>::nrows;
230  using Distribution2D<gno_t, scalar_t>::npRows;
231  using Distribution2D<gno_t, scalar_t>::npCols;
232 
233  Distribution2DVec(size_t nrows_,
234  const Teuchos::RCP<const Teuchos::Comm<int> > &comm_,
235  const Teuchos::ParameterList &params,
236  std::string &distributionfile)
237  : Distribution2D<gno_t, scalar_t>(nrows_, comm_, params) {
238  if (me == 0) std::cout << "\n 2DVec Distribution: "
239  << "\n np = " << np << std::endl;
240  std::ifstream fpin;
241  if (me == 0) {
242  fpin.open(distributionfile.c_str(), std::ios::in);
243  if (!fpin.is_open()) {
244  std::cout << "Error: distributionfile " << distributionfile
245  << " not found" << std::endl;
246  exit(-1);
247  }
248  }
249 
250  // Read the vector part assignment and broadcast it to all processes.
251  // Broadcast in chunks of bcastsize values.
252  // TODO: Make the vector part assignment more scalable instead of
253  // TODO: storing entire vector on every process.
254 
255  vecpart = new int[nrows];
256 
257  const int bcastsize = 1000000;
258 
259  gno_t start = 0;
260  int cnt = 0;
261  for (size_t i = 0; i < nrows; i++) {
262  if (me == 0) fpin >> vecpart[i];
263  cnt++;
264  if (cnt == bcastsize || i == nrows - 1) {
265  Teuchos::broadcast(*comm, 0, cnt, &(vecpart[start]));
266  start += cnt;
267  cnt = 0;
268  }
269  }
270 
271  if (me == 0) fpin.close();
272  }
273 
274  ~Distribution2DVec() { delete[] vecpart; }
275 
276  inline enum DistributionType DistType() { return TwoDVec; }
277 
278  bool Mine(gno_t i, gno_t j) {
279  return (me == (vecpart[i] % npRows + (vecpart[j] / npRows) * npRows));
280  }
281  inline bool Mine(gno_t i, gno_t j, int p) { return Mine(i, j); }
282 
283  inline bool VecMine(gno_t i) { return (vecpart[i] == me); }
284 
285  private:
286  int *vecpart;
287 };
288 
289 } // namespace Tpetra
290 #endif
void start()
Start the deep_copy counter.