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 
66 static void rendezvous_rank_get(HAVEWANT_t* data, int size,
67  HAVEWANT_t* &sdata, int* &scnt, int* &sdispl,
68  HAVEWANT_t* &rdata, int* &rcnt, int* &rdispl,
69  int (*rendezvous_rank)(HAVEWANT_t)
70 ){
71  int nhost = nrnmpi_numprocs;
72 
73  // count what gets sent
74  scnt = new int[nhost];
75  for (int i=0; i < nhost; ++i) { scnt[i] = 0; }
76  for (int i=0; i < size; ++i) {
77  int r = (*rendezvous_rank)(data[i]);
78  ++scnt[r];
79  }
80 
81  sdispl = cnt2displ(scnt);
82  rcnt = srccnt2destcnt(scnt);
83  rdispl = cnt2displ(rcnt);
84  sdata = new HAVEWANT_t[sdispl[nhost] + 1]; // ensure not 0 size
85  rdata = new HAVEWANT_t[rdispl[nhost] + 1]; // ensure not 0 size
86  // scatter data into sdata by recalculating scnt.
87  for (int i=0; i < nhost; ++i) { scnt[i] = 0; }
88  for (int i=0; i < size; ++i) {
89  int r = (*rendezvous_rank)(data[i]);
90  sdata[sdispl[r] + scnt[r]] = data[i];
91  ++scnt[r];
92  }
93  HAVEWANT_alltoallv(sdata, scnt, sdispl, rdata, rcnt, rdispl);
94 }
95 
96 static void have_to_want(
97  HAVEWANT_t* have, int have_size, HAVEWANT_t* want, int want_size,
98  HAVEWANT_t* &send_to_want, int* &send_to_want_cnt, int* &send_to_want_displ,
99  HAVEWANT_t* &recv_from_have, int* &recv_from_have_cnt, int* &recv_from_have_displ,
100  int (*rendezvous_rank)(HAVEWANT_t)
101 ){
102  // 1) Send have and want to the rendezvous ranks.
103  // 2) Rendezvous rank matches have and want.
104  // 3) Rendezvous ranks tell the want ranks which ranks own the keys
105  // 4) Ranks that want tell owner ranks where to send.
106 
107  int nhost = nrnmpi_numprocs;
108 
109  // 1) Send have and want to the rendezvous ranks.
110  HAVEWANT_t *have_s_data, *have_r_data;
111  int *have_s_cnt, *have_s_displ, *have_r_cnt, *have_r_displ;
112  rendezvous_rank_get(have, have_size,
113  have_s_data, have_s_cnt, have_s_displ,
114  have_r_data, have_r_cnt, have_r_displ,
115  rendezvous_rank
116  );
117  delete [] have_s_cnt;
118  delete [] have_s_displ;
119  delete [] have_s_data;
120  // assume it is an error if two ranks have the same key so create
121  // hash table of key2rank. Will also need it for matching have and want
122  HAVEWANT2Int havekey2rank = HAVEWANT2Int(have_r_displ[nhost]+1); //ensure not empty.
123  for (int r=0; r < nhost; ++r) {
124  for (int i=0; i < have_r_cnt[r]; ++i) {
125  HAVEWANT_t key = have_r_data[have_r_displ[r] + i];
126  if (havekey2rank.find(key) != havekey2rank.end()) {
127  hoc_execerr_ext("internal error in have_to_want: key %lld owned by multiple ranks\n", (long long)key);
128  }
129  havekey2rank[key] = r;
130  }
131  }
132  delete [] have_r_data;
133  delete [] have_r_cnt;
134  delete [] have_r_displ;
135 
136  HAVEWANT_t *want_s_data, *want_r_data;
137  int *want_s_cnt, *want_s_displ, *want_r_cnt, *want_r_displ;
138  rendezvous_rank_get(want, want_size,
139  want_s_data, want_s_cnt, want_s_displ,
140  want_r_data, want_r_cnt, want_r_displ,
141  rendezvous_rank
142  );
143 
144  // 2) Rendezvous rank matches have and want.
145  // we already have made the havekey2rank map.
146  // Create an array parallel to want_r_data which contains the ranks that
147  // have that data.
148  int n = want_r_displ[nhost];
149  int* want_r_ownerranks = new int[n];
150  for (int r=0; r < nhost; ++r) {
151  for (int i=0; i < want_r_cnt[r]; ++i) {
152  int ix = want_r_displ[r] + i;
153  HAVEWANT_t key = want_r_data[ix];
154  auto search = havekey2rank.find(key);
155  if (search == havekey2rank.end()) {
156  hoc_execerr_ext("internal error in have_to_want: key = %lld is wanted but does not exist\n", (long long)key);
157  }
158  want_r_ownerranks[ix] = search->second;
159  }
160  }
161  delete [] want_r_data;
162 
163  // 3) Rendezvous ranks tell the want ranks which ranks own the keys
164  // The ranks that want keys need to know the ranks that own those keys.
165  // The want_s_ownerranks will be parallel to the want_s_data.
166  // That is, each item defines the rank from which information associated
167  // with that key is coming from
168  int* want_s_ownerranks = new int[want_s_displ[nhost]];
169  if (nrn_sparse_partrans > 0)
170  {
171  nrnmpi_int_alltoallv_sparse(want_r_ownerranks, want_r_cnt, want_r_displ,
172  want_s_ownerranks, want_s_cnt, want_s_displ);
173  }
174  else
175  {
176  nrnmpi_int_alltoallv(want_r_ownerranks, want_r_cnt, want_r_displ,
177  want_s_ownerranks, want_s_cnt, want_s_displ);
178  }
179 
180  delete [] want_r_ownerranks;
181  delete [] want_r_cnt;
182  delete [] want_r_displ;
183 
184  // 4) Ranks that want tell owner ranks where to send.
185  // Finished with the rendezvous ranks. The ranks that want keys know the
186  // owner ranks for those keys. The next step is for the want ranks to
187  // tell the owner ranks where to send.
188  // The parallel want_s_ownerranks and want_s_data are now uselessly ordered
189  // by rendezvous rank. Reorganize so that want ranks can tell owner ranks
190  // what they want.
191  n = want_s_displ[nhost];
192  delete [] want_s_displ;
193  for (int i=0; i < nhost; ++i) { want_s_cnt[i] = 0; }
194  HAVEWANT_t* old_want_s_data = want_s_data;
195  want_s_data = new HAVEWANT_t[n];
196  // compute the counts
197  for (int i=0; i < n; ++i) {
198  int r = want_s_ownerranks[i];
199  ++want_s_cnt[r];
200  }
201  want_s_displ = cnt2displ(want_s_cnt);
202  for (int i=0; i < nhost; ++i) { want_s_cnt[i] = 0; } // recount while filling
203  for (int i=0; i < n; ++i) {
204  int r = want_s_ownerranks[i];
205  HAVEWANT_t key = old_want_s_data[i];
206  want_s_data[want_s_displ[r] + want_s_cnt[r]] = key;
207  ++want_s_cnt[r];
208  }
209  delete [] want_s_ownerranks;
210  delete [] old_want_s_data;
211  want_r_cnt = srccnt2destcnt(want_s_cnt);
212  want_r_displ = cnt2displ(want_r_cnt);
213  want_r_data = new HAVEWANT_t[want_r_displ[nhost]];
214  HAVEWANT_alltoallv(want_s_data, want_s_cnt, want_s_displ,
215  want_r_data, want_r_cnt, want_r_displ);
216  // now the want_r_data on the have_ranks are grouped according to the ranks
217  // that want those keys.
218 
219  send_to_want = want_r_data;
220  send_to_want_cnt = want_r_cnt;
221  send_to_want_displ = want_r_displ;
222  recv_from_have = want_s_data;
223  recv_from_have_cnt = want_s_cnt;
224  recv_from_have_displ = want_s_displ;
225 }
#define data
Definition: rbtqueue.cpp:49
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
int nrn_sparse_partrans
Definition: init.cpp:125
static double nhost(void *v)
Definition: ocbbs.cpp:230
static int default_rendezvous(HAVEWANT_t key)
Definition: have2want.cpp:47
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:96
void hoc_execerr_ext(const char *fmt,...)
printf style specification of hoc_execerror message.
Definition: fileio.cpp:958
int const size_t const size_t n
Definition: nrngsl.h:12
int nrnmpi_numprocs
#define cnt
Definition: spt2queue.cpp:19
#define key
Definition: spt2queue.cpp:20
#define HAVEWANT_t
Definition: have2want.cpp:43
static void nrnmpi_int_alltoallv(int *s, int *scnt, int *sdispl, int *r, int *rcnt, int *rdispl)
#define HAVEWANT2Int
Definition: partrans.cpp:670
#define i
Definition: md1redef.h:12
#define HAVEWANT_alltoallv
Definition: partrans.cpp:669