NEURON
have2want.h
Go to the documentation of this file.
1 /*
2 # =============================================================================
3 # Copyright (c) 2016 - 2021 Blue Brain Project/EPFL
4 #
5 # See top-level LICENSE file for details.
6 # =============================================================================
7 */
8 
9 /*
10 To be included by a file that desires rendezvous rank exchange functionality.
11 Need to define HAVEWANT_t, HAVEWANT_alltoallv, and HAVEWANT2Int
12 */
13 
14 #ifdef have2want_h
15 #error "This implementation can only be included once"
16 /* The static function names could involve a macro name. */
17 #endif
18 
19 #define have2want_h
20 
24 
25 /*
26 
27 A rank owns a set of HAVEWANT_t keys and wants information associated with
28 a set of HAVEWANT_t keys owned by unknown ranks. Owners do not know which
29 ranks want their information. Ranks that want info do not know which ranks
30 own that info.
31 
32 The have_to_want function returns two new vectors of keys along with
33 associated count and displacement vectors of length nrnmpi_numprocs and nrnmpi_numprocs+1
34 respectively. Note that a send_to_want_displ[i+1] =
35  send_to_want_cnt[i] + send_to_want_displ[i] .
36 
37 send_to_want[send_to_want_displ[i] to send_to_want_displ[i+1]] contains
38 the keys from this rank for which rank i wants information.
39 
40 recv_from_have[recv_from_have_displ[i] to recv_from_have_displ[i+1] contains
41 the keys from which rank i is sending information to this rank.
42 
43 Note that on rank i, the order of keys in the rank j area of send_to_want
44 is the same order of keys on rank j in the ith area in recv_from_have.
45 
46 The rendezvous_rank function is used to parallelize this computation
47 and minimize memory usage so that no single rank ever needs to know all keys.
48 */
49 
50 #ifndef HAVEWANT_t
51 #define HAVEWANT_t int
52 #endif
53 namespace coreneuron {
54 // round robin default rendezvous rank function
56  return key % nrnmpi_numprocs;
57 }
58 
59 static int* cnt2displ(int* cnt) {
60  int* displ = new int[nrnmpi_numprocs + 1];
61  displ[0] = 0;
62  for (int i = 0; i < nrnmpi_numprocs; ++i) {
63  displ[i + 1] = displ[i] + cnt[i];
64  }
65  return displ;
66 }
67 
68 static int* srccnt2destcnt(int* srccnt) {
69  int* destcnt = new int[nrnmpi_numprocs];
70 #if NRNMPI
72  nrnmpi_int_alltoall(srccnt, destcnt, 1);
73  } else
74 #endif
75  {
76  for (int i = 0; i < nrnmpi_numprocs; ++i) {
77  destcnt[i] = srccnt[i];
78  }
79  }
80  return destcnt;
81 }
82 
84  int size,
85  HAVEWANT_t*& sdata,
86  int*& scnt,
87  int*& sdispl,
88  HAVEWANT_t*& rdata,
89  int*& rcnt,
90  int*& rdispl,
91  int (*rendezvous_rank)(HAVEWANT_t)) {
92  // count what gets sent
93  scnt = new int[nrnmpi_numprocs];
94  for (int i = 0; i < nrnmpi_numprocs; ++i) {
95  scnt[i] = 0;
96  }
97  for (int i = 0; i < size; ++i) {
98  int r = (*rendezvous_rank)(data[i]);
99  ++scnt[r];
100  }
101 
102  sdispl = cnt2displ(scnt);
103  rcnt = srccnt2destcnt(scnt);
104  rdispl = cnt2displ(rcnt);
105  sdata = new HAVEWANT_t[sdispl[nrnmpi_numprocs]];
106  rdata = new HAVEWANT_t[rdispl[nrnmpi_numprocs]];
107  // scatter data into sdata by recalculating scnt.
108  for (int i = 0; i < nrnmpi_numprocs; ++i) {
109  scnt[i] = 0;
110  }
111  for (int i = 0; i < size; ++i) {
112  int r = (*rendezvous_rank)(data[i]);
113  sdata[sdispl[r] + scnt[r]] = data[i];
114  ++scnt[r];
115  }
116 #if NRNMPI
118  HAVEWANT_alltoallv(sdata, scnt, sdispl, rdata, rcnt, rdispl);
119  } else
120 #endif
121  {
122  for (int i = 0; i < sdispl[nrnmpi_numprocs]; ++i) {
123  rdata[i] = sdata[i];
124  }
125  }
126 }
127 
128 static void have_to_want(HAVEWANT_t* have,
129  int have_size,
130  HAVEWANT_t* want,
131  int want_size,
132  HAVEWANT_t*& send_to_want,
133  int*& send_to_want_cnt,
134  int*& send_to_want_displ,
135  HAVEWANT_t*& recv_from_have,
136  int*& recv_from_have_cnt,
137  int*& recv_from_have_displ,
138  int (*rendezvous_rank)(HAVEWANT_t)) {
139  // 1) Send have and want to the rendezvous ranks.
140  // 2) Rendezvous rank matches have and want.
141  // 3) Rendezvous ranks tell the want ranks which ranks own the keys
142  // 4) Ranks that want tell owner ranks where to send.
143 
144  // 1) Send have and want to the rendezvous ranks.
145  HAVEWANT_t *have_s_data, *have_r_data;
146  int *have_s_cnt, *have_s_displ, *have_r_cnt, *have_r_displ;
147  rendezvous_rank_get(have,
148  have_size,
149  have_s_data,
150  have_s_cnt,
151  have_s_displ,
152  have_r_data,
153  have_r_cnt,
154  have_r_displ,
156  // assume it is an error if two ranks have the same key so create
157  // hash table of key2rank. Will also need it for matching have and want
158  HAVEWANT2Int havekey2rank = HAVEWANT2Int();
159  for (int r = 0; r < nrnmpi_numprocs; ++r) {
160  for (int i = 0; i < have_r_cnt[r]; ++i) {
161  HAVEWANT_t key = have_r_data[have_r_displ[r] + i];
162  if (havekey2rank.find(key) != havekey2rank.end()) {
163  char buf[200];
164  std::snprintf(buf,
165  sizeof(buf),
166  "key %lld owned by multiple ranks\n",
167  (long long) key);
168  hoc_execerror(buf, 0);
169  }
170  havekey2rank[key] = r;
171  }
172  }
173  delete[] have_s_data;
174  delete[] have_s_cnt;
175  delete[] have_s_displ;
176  delete[] have_r_data;
177  delete[] have_r_cnt;
178  delete[] have_r_displ;
179 
180  HAVEWANT_t *want_s_data, *want_r_data;
181  int *want_s_cnt, *want_s_displ, *want_r_cnt, *want_r_displ;
182  rendezvous_rank_get(want,
183  want_size,
184  want_s_data,
185  want_s_cnt,
186  want_s_displ,
187  want_r_data,
188  want_r_cnt,
189  want_r_displ,
191 
192  // 2) Rendezvous rank matches have and want.
193  // we already have made the havekey2rank map.
194  // Create an array parallel to want_r_data which contains the ranks that
195  // have that data.
196  int n = want_r_displ[nrnmpi_numprocs];
197  int* want_r_ownerranks = new int[n];
198  for (int r = 0; r < nrnmpi_numprocs; ++r) {
199  for (int i = 0; i < want_r_cnt[r]; ++i) {
200  int ix = want_r_displ[r] + i;
201  HAVEWANT_t key = want_r_data[ix];
202  if (havekey2rank.find(key) == havekey2rank.end()) {
203  char buf[200];
204  std::snprintf(buf,
205  sizeof(buf),
206  "key = %lld is wanted but does not exist\n",
207  (long long) key);
208  hoc_execerror(buf, 0);
209  }
210  want_r_ownerranks[ix] = havekey2rank[key];
211  }
212  }
213  delete[] want_r_data;
214 
215  // 3) Rendezvous ranks tell the want ranks which ranks own the keys
216  // The ranks that want keys need to know the ranks that own those keys.
217  // The want_s_ownerranks will be parallel to the want_s_data.
218  // That is, each item defines the rank from which information associated
219  // with that key is coming from
220  int* want_s_ownerranks = new int[want_s_displ[nrnmpi_numprocs]];
221 #if NRNMPI
223  nrnmpi_int_alltoallv(want_r_ownerranks,
224  want_r_cnt,
225  want_r_displ,
226  want_s_ownerranks,
227  want_s_cnt,
228  want_s_displ);
229  } else
230 #endif
231  {
232  for (int i = 0; i < want_r_displ[nrnmpi_numprocs]; ++i) {
233  want_s_ownerranks[i] = want_r_ownerranks[i];
234  }
235  }
236  delete[] want_r_ownerranks;
237  delete[] want_r_cnt;
238  delete[] want_r_displ;
239 
240  // 4) Ranks that want tell owner ranks where to send.
241  // Finished with the rendezvous ranks. The ranks that want keys know the
242  // owner ranks for those keys. The next step is for the want ranks to
243  // tell the owner ranks where to send.
244  // The parallel want_s_ownerranks and want_s_data are now uselessly ordered
245  // by rendezvous rank. Reorganize so that want ranks can tell owner ranks
246  // what they want.
247  n = want_s_displ[nrnmpi_numprocs];
248  delete[] want_s_displ;
249  for (int i = 0; i < nrnmpi_numprocs; ++i) {
250  want_s_cnt[i] = 0;
251  }
252  HAVEWANT_t* old_want_s_data = want_s_data;
253  want_s_data = new HAVEWANT_t[n];
254  // compute the counts
255  for (int i = 0; i < n; ++i) {
256  int r = want_s_ownerranks[i];
257  ++want_s_cnt[r];
258  }
259  want_s_displ = cnt2displ(want_s_cnt);
260  for (int i = 0; i < nrnmpi_numprocs; ++i) {
261  want_s_cnt[i] = 0;
262  } // recount while filling
263  for (int i = 0; i < n; ++i) {
264  int r = want_s_ownerranks[i];
265  HAVEWANT_t key = old_want_s_data[i];
266  want_s_data[want_s_displ[r] + want_s_cnt[r]] = key;
267  ++want_s_cnt[r];
268  }
269  delete[] want_s_ownerranks;
270  delete[] old_want_s_data;
271  want_r_cnt = srccnt2destcnt(want_s_cnt);
272  want_r_displ = cnt2displ(want_r_cnt);
273  want_r_data = new HAVEWANT_t[want_r_displ[nrnmpi_numprocs]];
274 #if NRNMPI
277  want_s_data, want_s_cnt, want_s_displ, want_r_data, want_r_cnt, want_r_displ);
278  } else
279 #endif
280  {
281  for (int i = 0; i < want_s_displ[nrnmpi_numprocs]; ++i) {
282  want_r_data[i] = want_s_data[i];
283  }
284  }
285  // now the want_r_data on the have_ranks are grouped according to the ranks
286  // that want those keys.
287 
288  send_to_want = want_r_data;
289  send_to_want_cnt = want_r_cnt;
290  send_to_want_displ = want_r_displ;
291  recv_from_have = want_s_data;
292  recv_from_have_cnt = want_s_cnt;
293  recv_from_have_displ = want_s_displ;
294 }
295 } // namespace coreneuron
static void nrnmpi_int_alltoallv(const int *s, const int *scnt, const int *sdispl, int *r, int *rcnt, int *rdispl)
#define cnt
Definition: tqueue.hpp:44
#define key
Definition: tqueue.hpp:45
#define i
Definition: md1redef.h:19
char buf[512]
Definition: init.cpp:13
#define HAVEWANT_t
Definition: have2want.h:51
int rendezvous_rank(const T &key)
Definition: have2want.hpp:33
THIS FILE IS AUTO GENERATED DONT MODIFY IT.
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.h:128
static int default_rendezvous(HAVEWANT_t key)
Definition: have2want.h:55
void hoc_execerror(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:39
static int * srccnt2destcnt(int *srccnt)
Definition: have2want.h:68
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.h:83
static int * cnt2displ(int *cnt)
Definition: have2want.h:59
corenrn_parameters corenrn_param
Printing method.
int const size_t const size_t n
Definition: nrngsl.h:10
#define HAVEWANT2Int
#define HAVEWANT_alltoallv
bool mpi_enable
Initialization seed for random number generator (int)