NEURON
have2want.cpp
Go to the documentation of this file.
1 /*
2 To be included by a file that desires rendezvous rank exchange functionality.
3 Need to define HAVEWANT_t, HAVEWANT_alltoallv, and HAVEWANT2Int
4 The latter is a map or unordered_map.
5 E.g. std::unordered_map<size_t, int>
6 */
7 
8 #ifdef have2want_cpp
9 #error "This implementation can only be included once"
10 // The static function names used to involve a macro name (NrnHash) but now,
11 // with the use of std::..., it may be the case this could be included
12 // multiple times or even transformed into a template.
13 #endif
14 
15 #define have2want_cpp
16 
17 /*
18 
19 A rank owns a set of HAVEWANT_t keys and wants information associated with
20 a set of HAVEWANT_t keys owned by unknown ranks. Owners do not know which
21 ranks want their information. Ranks that want info do not know which ranks
22 own that info.
23 
24 The have_to_want function returns two new vectors of keys along with
25 associated count and displacement vectors of length nhost and nhost+1
26 respectively. Note that a send_to_want_displ[i+1] =
27  send_to_want_cnt[i] + send_to_want_displ[i] .
28 
29 send_to_want[send_to_want_displ[i] to send_to_want_displ[i+1]] contains
30 the keys from this rank for which rank i wants information.
31 
32 recv_from_have[recv_from_have_displ[i] to recv_from_have_displ[i+1] contains
33 the keys from which rank i is sending information to this rank.
34 
35 Note that on rank i, the order of keys in the rank j area of send_to_want
36 is the same order of keys on rank j in the ith area in recv_from_have.
37 
38 The rendezvous_rank function is used to parallelize this computation
39 and minimize memory usage so that no single rank ever needs to know all keys.
40 */
41 
42 #ifndef HAVEWANT_t
43 #define HAVEWANT_t int
44 #endif
45 
46 // round robin default rendezvous rank function
48  return key % nrnmpi_numprocs;
49 }
50 
51 static int* cnt2displ(int* cnt) {
52  int* displ = new int[nrnmpi_numprocs + 1];
53  displ[0] = 0;
54  for (int i = 0; i < nrnmpi_numprocs; ++i) {
55  displ[i + 1] = displ[i] + cnt[i];
56  }
57  return displ;
58 }
59 
60 static int* srccnt2destcnt(int* srccnt) {
61  int* destcnt = new int[nrnmpi_numprocs];
62  nrnmpi_int_alltoall(srccnt, destcnt, 1);
63  return destcnt;
64 }
65 
67  int size,
68  HAVEWANT_t*& sdata,
69  int*& scnt,
70  int*& sdispl,
71  HAVEWANT_t*& rdata,
72  int*& rcnt,
73  int*& rdispl,
74  int (*rendezvous_rank)(HAVEWANT_t)) {
75  int nhost = nrnmpi_numprocs;
76 
77  // count what gets sent
78  scnt = new int[nhost];
79  for (int i = 0; i < nhost; ++i) {
80  scnt[i] = 0;
81  }
82  for (int i = 0; i < size; ++i) {
83  int r = (*rendezvous_rank)(data[i]);
84  ++scnt[r];
85  }
86 
87  sdispl = cnt2displ(scnt);
88  rcnt = srccnt2destcnt(scnt);
89  rdispl = cnt2displ(rcnt);
90  sdata = new HAVEWANT_t[sdispl[nhost] + 1]; // ensure not 0 size
91  rdata = new HAVEWANT_t[rdispl[nhost] + 1]; // ensure not 0 size
92  // scatter data into sdata by recalculating scnt.
93  for (int i = 0; i < nhost; ++i) {
94  scnt[i] = 0;
95  }
96  for (int i = 0; i < size; ++i) {
97  int r = (*rendezvous_rank)(data[i]);
98  sdata[sdispl[r] + scnt[r]] = data[i];
99  ++scnt[r];
100  }
101  HAVEWANT_alltoallv(sdata, scnt, sdispl, rdata, rcnt, rdispl);
102 }
103 
104 static void have_to_want(HAVEWANT_t* have,
105  int have_size,
106  HAVEWANT_t* want,
107  int want_size,
108  HAVEWANT_t*& send_to_want,
109  int*& send_to_want_cnt,
110  int*& send_to_want_displ,
111  HAVEWANT_t*& recv_from_have,
112  int*& recv_from_have_cnt,
113  int*& recv_from_have_displ,
114  int (*rendezvous_rank)(HAVEWANT_t)) {
115  // 1) Send have and want to the rendezvous ranks.
116  // 2) Rendezvous rank matches have and want.
117  // 3) Rendezvous ranks tell the want ranks which ranks own the keys
118  // 4) Ranks that want tell owner ranks where to send.
119 
120  int nhost = nrnmpi_numprocs;
121 
122  // 1) Send have and want to the rendezvous ranks.
123  HAVEWANT_t *have_s_data, *have_r_data;
124  int *have_s_cnt, *have_s_displ, *have_r_cnt, *have_r_displ;
125  rendezvous_rank_get(have,
126  have_size,
127  have_s_data,
128  have_s_cnt,
129  have_s_displ,
130  have_r_data,
131  have_r_cnt,
132  have_r_displ,
133  rendezvous_rank);
134  delete[] have_s_cnt;
135  delete[] have_s_displ;
136  delete[] have_s_data;
137  // assume it is an error if two ranks have the same key so create
138  // hash table of key2rank. Will also need it for matching have and want
139  HAVEWANT2Int havekey2rank = HAVEWANT2Int(have_r_displ[nhost] + 1); // ensure not empty.
140  for (int r = 0; r < nhost; ++r) {
141  for (int i = 0; i < have_r_cnt[r]; ++i) {
142  HAVEWANT_t key = have_r_data[have_r_displ[r] + i];
143  if (havekey2rank.find(key) != havekey2rank.end()) {
145  "internal error in have_to_want: key %lld owned by multiple ranks\n",
146  (long long) key);
147  }
148  havekey2rank[key] = r;
149  }
150  }
151  delete[] have_r_data;
152  delete[] have_r_cnt;
153  delete[] have_r_displ;
154 
155  HAVEWANT_t *want_s_data, *want_r_data;
156  int *want_s_cnt, *want_s_displ, *want_r_cnt, *want_r_displ;
157  rendezvous_rank_get(want,
158  want_size,
159  want_s_data,
160  want_s_cnt,
161  want_s_displ,
162  want_r_data,
163  want_r_cnt,
164  want_r_displ,
165  rendezvous_rank);
166 
167  // 2) Rendezvous rank matches have and want.
168  // we already have made the havekey2rank map.
169  // Create an array parallel to want_r_data which contains the ranks that
170  // have that data.
171  int n = want_r_displ[nhost];
172  int* want_r_ownerranks = new int[n];
173  for (int r = 0; r < nhost; ++r) {
174  for (int i = 0; i < want_r_cnt[r]; ++i) {
175  int ix = want_r_displ[r] + i;
176  HAVEWANT_t key = want_r_data[ix];
177  auto search = havekey2rank.find(key);
178  if (search == havekey2rank.end()) {
180  "internal error in have_to_want: key = %lld is wanted but does not exist\n",
181  (long long) key);
182  }
183  want_r_ownerranks[ix] = search->second;
184  }
185  }
186  delete[] want_r_data;
187 
188  // 3) Rendezvous ranks tell the want ranks which ranks own the keys
189  // The ranks that want keys need to know the ranks that own those keys.
190  // The want_s_ownerranks will be parallel to the want_s_data.
191  // That is, each item defines the rank from which information associated
192  // with that key is coming from
193  int* want_s_ownerranks = new int[want_s_displ[nhost]];
194  if (nrn_sparse_partrans > 0) {
195  nrnmpi_int_alltoallv_sparse(want_r_ownerranks,
196  want_r_cnt,
197  want_r_displ,
198  want_s_ownerranks,
199  want_s_cnt,
200  want_s_displ);
201  } else {
202  nrnmpi_int_alltoallv(want_r_ownerranks,
203  want_r_cnt,
204  want_r_displ,
205  want_s_ownerranks,
206  want_s_cnt,
207  want_s_displ);
208  }
209 
210  delete[] want_r_ownerranks;
211  delete[] want_r_cnt;
212  delete[] want_r_displ;
213 
214  // 4) Ranks that want tell owner ranks where to send.
215  // Finished with the rendezvous ranks. The ranks that want keys know the
216  // owner ranks for those keys. The next step is for the want ranks to
217  // tell the owner ranks where to send.
218  // The parallel want_s_ownerranks and want_s_data are now uselessly ordered
219  // by rendezvous rank. Reorganize so that want ranks can tell owner ranks
220  // what they want.
221  n = want_s_displ[nhost];
222  delete[] want_s_displ;
223  for (int i = 0; i < nhost; ++i) {
224  want_s_cnt[i] = 0;
225  }
226  HAVEWANT_t* old_want_s_data = want_s_data;
227  want_s_data = new HAVEWANT_t[n];
228  // compute the counts
229  for (int i = 0; i < n; ++i) {
230  int r = want_s_ownerranks[i];
231  ++want_s_cnt[r];
232  }
233  want_s_displ = cnt2displ(want_s_cnt);
234  for (int i = 0; i < nhost; ++i) {
235  want_s_cnt[i] = 0;
236  } // recount while filling
237  for (int i = 0; i < n; ++i) {
238  int r = want_s_ownerranks[i];
239  HAVEWANT_t key = old_want_s_data[i];
240  want_s_data[want_s_displ[r] + want_s_cnt[r]] = key;
241  ++want_s_cnt[r];
242  }
243  delete[] want_s_ownerranks;
244  delete[] old_want_s_data;
245  want_r_cnt = srccnt2destcnt(want_s_cnt);
246  want_r_displ = cnt2displ(want_r_cnt);
247  want_r_data = new HAVEWANT_t[want_r_displ[nhost]];
249  want_s_data, want_s_cnt, want_s_displ, want_r_data, want_r_cnt, want_r_displ);
250  // now the want_r_data on the have_ranks are grouped according to the ranks
251  // that want those keys.
252 
253  send_to_want = want_r_data;
254  send_to_want_cnt = want_r_cnt;
255  send_to_want_displ = want_r_displ;
256  recv_from_have = want_s_data;
257  recv_from_have_cnt = want_s_cnt;
258  recv_from_have_displ = want_s_displ;
259 }
static void nrnmpi_int_alltoallv(int *s, int *scnt, int *sdispl, int *r, int *rcnt, int *rdispl)
void hoc_execerr_ext(const char *fmt,...)
printf style specification of hoc_execerror message.
Definition: fileio.cpp:931
static void rendezvous_rank_get(HAVEWANT_t *data, int size, HAVEWANT_t *&sdata, int *&scnt, int *&sdispl, HAVEWANT_t *&rdata, int *&rcnt, int *&rdispl, int(*rendezvous_rank)(HAVEWANT_t))
Definition: have2want.cpp:66
static int * cnt2displ(int *cnt)
Definition: have2want.cpp:51
static int * srccnt2destcnt(int *srccnt)
Definition: have2want.cpp:60
static void have_to_want(HAVEWANT_t *have, int have_size, HAVEWANT_t *want, int want_size, HAVEWANT_t *&send_to_want, int *&send_to_want_cnt, int *&send_to_want_displ, HAVEWANT_t *&recv_from_have, int *&recv_from_have_cnt, int *&recv_from_have_displ, int(*rendezvous_rank)(HAVEWANT_t))
Definition: have2want.cpp:104
static int default_rendezvous(HAVEWANT_t key)
Definition: have2want.cpp:47
#define HAVEWANT_t
Definition: have2want.cpp:43
#define i
Definition: md1redef.h:12
int const size_t const size_t n
Definition: nrngsl.h:11
int nrnmpi_numprocs
int nrn_sparse_partrans
Definition: init.cpp:101
static double nhost(void *v)
Definition: ocbbs.cpp:232
#define HAVEWANT2Int
Definition: partrans.cpp:688
#define HAVEWANT_alltoallv
Definition: partrans.cpp:687
#define data
Definition: rbtqueue.cpp:49
#define cnt
Definition: spt2queue.cpp:19
#define key
Definition: spt2queue.cpp:20