1 /******************************************************************************
2 * Copyright 1998-2019 Lawrence Livermore National Security, LLC and other
3 * HYPRE Project Developers. See the top-level COPYRIGHT file for details.
4 *
5 * SPDX-License-Identifier: (Apache-2.0 OR MIT)
6 ******************************************************************************/
7
8 /* see exchange_data.README for additional information */
9 /* AHB 6/04 */
10
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <math.h>
14
15 #include "_hypre_utilities.h"
16
17 /*---------------------------------------------------
18 * hypre_CreateBinaryTree()
19 * Get the processors position in the binary tree (i.e.,
20 * its children and parent processor ids)
21 *----------------------------------------------------*/
22
hypre_CreateBinaryTree(HYPRE_Int myid,HYPRE_Int num_procs,hypre_BinaryTree * tree)23 HYPRE_Int hypre_CreateBinaryTree(HYPRE_Int myid, HYPRE_Int num_procs,
24 hypre_BinaryTree *tree)
25 {
26 HYPRE_Int i, proc, size=0;
27 HYPRE_Int *tmp_child_id;
28 HYPRE_Int num=0, parent = 0;
29
30 /* initialize*/
31 proc = myid;
32
33 /*how many children can a processor have?*/
34 for (i = 1; i < num_procs; i *= 2)
35 {
36 size++;
37 }
38
39 /* allocate space */
40 tmp_child_id = hypre_TAlloc(HYPRE_Int, size, HYPRE_MEMORY_HOST);
41
42 /* find children and parent */
43 for (i = 1; i < num_procs; i *= 2)
44 {
45 if ( (proc % 2) == 0)
46 {
47 if( (myid + i) < num_procs )
48 {
49 tmp_child_id[num] = myid + i;
50 num++;
51 }
52 proc /= 2;
53 }
54 else
55 {
56 parent = myid - i;
57 break;
58 }
59
60 }
61
62 hypre_BinaryTreeParentId(tree) = parent;
63 hypre_BinaryTreeNumChild(tree) = num;
64 hypre_BinaryTreeChildIds(tree) = tmp_child_id;
65
66 return hypre_error_flag;
67 }
68
69 /*---------------------------------------------------
70 * hypre_DestroyBinaryTree()
71 * Destroy storage created by createBinaryTree
72 *----------------------------------------------------*/
hypre_DestroyBinaryTree(hypre_BinaryTree * tree)73 HYPRE_Int hypre_DestroyBinaryTree(hypre_BinaryTree *tree)
74 {
75
76 hypre_TFree(hypre_BinaryTreeChildIds(tree), HYPRE_MEMORY_HOST);
77
78 return hypre_error_flag;
79 }
80
81 /*---------------------------------------------------
82 * hypre_DataExchangeList()
83 * This function is for sending a list of messages ("contacts" to
84 * a list of processors. The receiving processors
85 * do not know how many messages they are getting. The
86 * sending process expects a "response" (either a confirmation or
87 * some sort of data back from the receiving processor).
88 *----------------------------------------------------*/
89
90 /* should change to where the buffers for sending and receiving are voids
91 instead of ints - then cast accordingly */
92
hypre_DataExchangeList(HYPRE_Int num_contacts,HYPRE_Int * contact_proc_list,void * contact_send_buf,HYPRE_Int * contact_send_buf_starts,HYPRE_Int contact_obj_size,HYPRE_Int response_obj_size,hypre_DataExchangeResponse * response_obj,HYPRE_Int max_response_size,HYPRE_Int rnum,MPI_Comm comm,void ** p_response_recv_buf,HYPRE_Int ** p_response_recv_buf_starts)93 HYPRE_Int hypre_DataExchangeList(HYPRE_Int num_contacts,
94 HYPRE_Int *contact_proc_list,
95 void *contact_send_buf,
96 HYPRE_Int *contact_send_buf_starts,
97 HYPRE_Int contact_obj_size,
98 HYPRE_Int response_obj_size,
99 hypre_DataExchangeResponse *response_obj,
100 HYPRE_Int max_response_size,
101 HYPRE_Int rnum, MPI_Comm comm,
102 void **p_response_recv_buf,
103 HYPRE_Int **p_response_recv_buf_starts)
104 {
105 /*-------------------------------------------
106 * parameters:
107 *
108 * num_contacts = how many procs to contact
109 * contact_proc_list = list of processors to contact
110 * contact_send_buf = array of data to send
111 * contact_send_buf_starts = index for contact_send_buf corresponding to
112 * contact_proc_list
113 * contact_obj_size = sizeof() one obj in contact list
114
115 * response_obj_size = sizeof() one obj in response_recv_buf
116 * response_obj = this will give us the function we need to
117 * fill the reponse as well as
118 * any data we might need to accomplish that
119 * max_response_size = max size of a single response expected (do NOT
120 * need to be an absolute upper bound)
121 * rnum = two consequentive exchanges should have different
122 * rnums. Alternate rnum = 1
123 * and rnum=2 - these flags will be even (so odd
124 * numbered tags could be used in calling code)
125 * p_response_recv_buf = where to receive the reponses - will be allocated
126 * in this function
127 * p_response_recv_buf_starts = index of p_response_buf corresponding to
128 * contact_buf_list - will be allocated here
129
130 *-------------------------------------------*/
131
132 HYPRE_Int num_procs, myid;
133 HYPRE_Int i;
134 HYPRE_Int terminate, responses_complete;
135 HYPRE_Int children_complete;
136 HYPRE_Int contact_flag;
137 HYPRE_Int proc;
138 HYPRE_Int contact_size;
139
140 HYPRE_Int size, post_size, copy_size;
141 HYPRE_Int total_size, count;
142
143 void *start_ptr = NULL, *index_ptr=NULL;
144 HYPRE_Int *int_ptr=NULL;
145
146 void *response_recv_buf = NULL;
147 void *send_response_buf = NULL;
148
149 HYPRE_Int *response_recv_buf_starts = NULL;
150 void *initial_recv_buf = NULL;
151
152 void *recv_contact_buf = NULL;
153 HYPRE_Int recv_contact_buf_size = 0;
154
155 HYPRE_Int response_message_size = 0;
156
157 HYPRE_Int overhead;
158
159 HYPRE_Int max_response_size_bytes;
160
161 HYPRE_Int max_response_total_bytes;
162
163 void **post_array = NULL; /*this must be set to null or realloc will crash */
164 HYPRE_Int post_array_storage = 0;
165 HYPRE_Int post_array_size = 0;
166 HYPRE_Int num_post_recvs =0;
167
168 void **contact_ptrs = NULL, **response_ptrs=NULL, **post_ptrs=NULL;
169
170 hypre_BinaryTree tree;
171
172 hypre_MPI_Request *response_requests, *contact_requests;
173 hypre_MPI_Status *response_statuses, *contact_statuses;
174
175 hypre_MPI_Request *post_send_requests = NULL, *post_recv_requests = NULL;
176 hypre_MPI_Status *post_send_statuses = NULL, *post_recv_statuses = NULL;
177
178 hypre_MPI_Request *term_requests, term_request1, request_parent;
179 hypre_MPI_Status *term_statuses, term_status1, status_parent;
180 hypre_MPI_Status status, fill_status;
181
182 const HYPRE_Int contact_tag = 1000*rnum;
183 const HYPRE_Int response_tag = 1002*rnum;
184 const HYPRE_Int term_tag = 1004*rnum;
185 const HYPRE_Int post_tag = 1006*rnum;
186
187 hypre_MPI_Comm_size(comm, &num_procs );
188 hypre_MPI_Comm_rank(comm, &myid );
189
190 /* ---------initializations ----------------*/
191
192 /* if the response_obj_size or contact_obj_size is 0, set to sizeof(HYPRE_Int) */
193 if (!response_obj_size) response_obj_size = sizeof(HYPRE_Int);
194 if (!contact_obj_size) contact_obj_size = sizeof(HYPRE_Int);
195
196 max_response_size_bytes = max_response_size*response_obj_size;
197
198
199 /* pre-allocate the max space for responding to contacts */
200 overhead = ceil((HYPRE_Real) sizeof(HYPRE_Int)/response_obj_size); /*for appending an integer*/
201
202 max_response_total_bytes = (max_response_size+overhead)*response_obj_size;
203
204 response_obj->send_response_overhead = overhead;
205 response_obj->send_response_storage = max_response_size;
206
207 /*send_response_buf = hypre_MAlloc(max_response_total_bytes);*/
208 send_response_buf = hypre_CTAlloc(char, (max_response_size+overhead)*response_obj_size, HYPRE_MEMORY_HOST);
209
210 /*allocate space for inital recv array for the responses - give each processor
211 size max_response_size */
212
213 initial_recv_buf = hypre_TAlloc(char, max_response_total_bytes*num_contacts, HYPRE_MEMORY_HOST);
214 response_recv_buf_starts = hypre_CTAlloc(HYPRE_Int, num_contacts+1, HYPRE_MEMORY_HOST);
215
216 contact_ptrs = hypre_TAlloc( void *, num_contacts, HYPRE_MEMORY_HOST);
217 response_ptrs = hypre_TAlloc(void *, num_contacts, HYPRE_MEMORY_HOST);
218
219 /*-------------SEND CONTACTS AND POST RECVS FOR RESPONSES---*/
220
221 for (i=0; i<= num_contacts; i++)
222 {
223 response_recv_buf_starts[i] = i*(max_response_size+overhead);
224 }
225
226 /* Send "contact" messages to the list of processors and
227 pre-post receives to wait for their response*/
228
229 responses_complete = 1;
230 if (num_contacts > 0 )
231 {
232 responses_complete = 0;
233 response_requests = hypre_CTAlloc(hypre_MPI_Request, num_contacts, HYPRE_MEMORY_HOST);
234 response_statuses = hypre_CTAlloc(hypre_MPI_Status, num_contacts, HYPRE_MEMORY_HOST);
235 contact_requests = hypre_CTAlloc(hypre_MPI_Request, num_contacts, HYPRE_MEMORY_HOST);
236 contact_statuses = hypre_CTAlloc(hypre_MPI_Status, num_contacts, HYPRE_MEMORY_HOST);
237
238 /* post receives - could be confirmation or data*/
239 /* the size to post is max_response_total_bytes*/
240
241 for (i=0; i< num_contacts; i++)
242 {
243 /* response_ptrs[i] = initial_recv_buf + i*max_response_total_bytes ; */
244 response_ptrs[i] = (void *)((char *) initial_recv_buf +
245 i*max_response_total_bytes) ;
246
247 hypre_MPI_Irecv(response_ptrs[i], max_response_total_bytes,
248 hypre_MPI_BYTE, contact_proc_list[i],
249 response_tag, comm, &response_requests[i]);
250 }
251
252 /* send out contact messages */
253 start_ptr = contact_send_buf;
254 for (i=0; i< num_contacts; i++)
255 {
256 contact_ptrs[i] = start_ptr;
257 size = contact_send_buf_starts[i+1] - contact_send_buf_starts[i] ;
258 hypre_MPI_Isend(contact_ptrs[i], size*contact_obj_size,
259 hypre_MPI_BYTE, contact_proc_list[i],
260 contact_tag, comm, &contact_requests[i]);
261 /* start_ptr += (size*contact_obj_size); */
262 start_ptr = (void *) ((char *) start_ptr + (size*contact_obj_size));
263 }
264 }
265
266 /*------------BINARY TREE-----------------------*/
267
268 /*Now let's find out our binary tree information and
269 initialize for the termination check sweep */
270 terminate = 1; /*indicates whether we can stop probing for contact */
271 children_complete = 1;/*indicates whether we have recv. term messages
272 from our children*/
273
274 if (num_procs > 1)
275 {
276 hypre_CreateBinaryTree(myid, num_procs, &tree);
277
278 /* we will get a message from all of our children when they
279 have received responses for all of their contacts.
280 So post receives now */
281
282 term_requests = hypre_CTAlloc(hypre_MPI_Request, tree.num_child, HYPRE_MEMORY_HOST);
283 term_statuses = hypre_CTAlloc(hypre_MPI_Status, tree.num_child, HYPRE_MEMORY_HOST);
284
285 for (i=0; i< tree.num_child; i++)
286 {
287 hypre_MPI_Irecv(NULL, 0, HYPRE_MPI_INT, tree.child_id[i], term_tag, comm,
288 &term_requests[i]);
289 }
290
291 terminate = 0;
292
293 children_complete = 0;
294 }
295 else if (num_procs ==1 && num_contacts > 0 ) /* added 11/08 */
296 {
297 terminate = 0;
298 }
299
300 /*---------PROBE LOOP-----------------------------------------*/
301
302 /*Look for incoming contact messages - don't know how many I will get!*/
303
304 while (!terminate)
305 {
306 /* did I receive any contact messages? */
307 hypre_MPI_Iprobe(hypre_MPI_ANY_SOURCE, contact_tag, comm,
308 &contact_flag, &status);
309
310 while (contact_flag)
311 {
312 /* received contacts - from who and what do we do ?*/
313 proc = status.hypre_MPI_SOURCE;
314 hypre_MPI_Get_count(&status, hypre_MPI_BYTE, &contact_size);
315
316 contact_size = contact_size/contact_obj_size;
317
318 /*---------------FILL RESPONSE ------------------------*/
319
320 /*first receive the contact buffer - then call a function
321 to determine how to populate the send buffer for the reponse*/
322
323 /* do we have enough space to recv it? */
324 if(contact_size > recv_contact_buf_size)
325 {
326 recv_contact_buf = hypre_TReAlloc((char*)recv_contact_buf,
327 char, contact_obj_size*contact_size, HYPRE_MEMORY_HOST);
328 recv_contact_buf_size = contact_size;
329 }
330
331 /* this must be blocking - can't fill recv without the buffer*/
332 hypre_MPI_Recv(recv_contact_buf, contact_size*contact_obj_size,
333 hypre_MPI_BYTE, proc, contact_tag, comm, &fill_status);
334
335 response_obj->fill_response(recv_contact_buf, contact_size, proc,
336 response_obj, comm, &send_response_buf,
337 &response_message_size );
338
339 /* we need to append the size of the send obj */
340 /* first we copy out any part that may be needed to send later so we don't overwrite */
341 post_size = response_message_size - max_response_size;
342 if (post_size > 0) /*we will need to send the extra information later */
343 {
344 /*hypre_printf("myid = %d, post_size = %d\n", myid, post_size);*/
345
346 if (post_array_size == post_array_storage)
347
348 {
349 /* allocate room for more posts - add 20*/
350 post_array_storage += 20;
351 post_array = hypre_TReAlloc(post_array, void *, post_array_storage, HYPRE_MEMORY_HOST);
352 post_send_requests =
353 hypre_TReAlloc(post_send_requests, hypre_MPI_Request,
354 post_array_storage, HYPRE_MEMORY_HOST);
355 }
356 /* allocate space for the data this post only*/
357 /* this should not happen often (unless a poor max_size has been chosen)
358 - so we will allocate space for the data as needed */
359 size = post_size*response_obj_size;
360 post_array[post_array_size] = hypre_TAlloc(char, size, HYPRE_MEMORY_HOST);
361 /* index_ptr = send_response_buf + max_response_size_bytes */;
362 index_ptr = (void *) ((char *) send_response_buf +
363 max_response_size_bytes);
364
365 hypre_TMemcpy(post_array[post_array_size], index_ptr, char, size, HYPRE_MEMORY_HOST, HYPRE_MEMORY_HOST);
366
367 /*now post any part of the message that is too long with a non-blocking
368 send and a different tag */
369
370 hypre_MPI_Isend(post_array[post_array_size], size,
371 hypre_MPI_BYTE, proc, post_tag,
372 /*hypre_MPI_COMM_WORLD, */
373 comm,
374 &post_send_requests[post_array_size]);
375
376 post_array_size++;
377 }
378
379 /*now append the size information into the overhead storage */
380 /* index_ptr = send_response_buf + max_response_size_bytes; */
381 index_ptr = (void *) ((char *) send_response_buf +
382 max_response_size_bytes);
383
384 hypre_TMemcpy(index_ptr, &response_message_size, HYPRE_Int, 1, HYPRE_MEMORY_HOST, HYPRE_MEMORY_HOST);
385
386 /*send the block of data that includes the overhead */
387 /* this is a blocking send - the recv has already been posted */
388 hypre_MPI_Send(send_response_buf, max_response_total_bytes,
389 hypre_MPI_BYTE, proc, response_tag, comm);
390
391 /*--------------------------------------------------------------*/
392
393 /* look for any more contact messages*/
394 hypre_MPI_Iprobe(hypre_MPI_ANY_SOURCE, contact_tag, comm,
395 &contact_flag, &status);
396 }
397
398 /* no more contact messages waiting - either
399 (1) check to see if we have received all of our response messages
400 (2) participate in termination (check for messages from children)
401 (3) participate in termination sweep (check for message from parent) */
402
403 if (!responses_complete)
404 {
405 hypre_MPI_Testall(num_contacts, response_requests, &responses_complete,
406 response_statuses);
407 if (responses_complete && num_procs == 1) terminate = 1; /*added 11/08 */
408
409 }
410 else if(!children_complete) /* have all of our children received all of their
411 response messages?*/
412 {
413 hypre_MPI_Testall(tree.num_child, term_requests, &children_complete,
414 term_statuses);
415
416 /* if we have gotten term messages from all of our children, send a term
417 message to our parent. Then post a receive to hear back from parent */
418 if (children_complete & (myid > 0)) /*root does not have a parent*/
419 {
420 hypre_MPI_Isend(NULL, 0, HYPRE_MPI_INT, tree.parent_id, term_tag,
421 comm, &request_parent);
422
423 hypre_MPI_Irecv(NULL, 0, HYPRE_MPI_INT, tree.parent_id, term_tag,
424 comm, &term_request1);
425 }
426 }
427 else /*have we gotten a term message from our parent? */
428 {
429 if (myid == 0) /* root doesn't have a parent */
430 {
431 terminate = 1;
432 }
433 else
434 {
435 hypre_MPI_Test(&term_request1, &terminate, &term_status1);
436 }
437 if (terminate) /*tell children to terminate */
438 {
439 if (myid > 0 ) hypre_MPI_Wait(&request_parent, &status_parent);
440
441 for (i=0; i< tree.num_child; i++)
442 { /*a blocking send - recv has been posted already*/
443 hypre_MPI_Send(NULL, 0, HYPRE_MPI_INT, tree.child_id[i],
444 term_tag, comm);
445 }
446 }
447 }
448 }
449
450 /* end of (!terminate) loop */
451
452 /* ----some clean up before post-processing ----*/
453 if (recv_contact_buf_size > 0)
454 {
455 hypre_TFree(recv_contact_buf, HYPRE_MEMORY_HOST);
456 }
457
458 hypre_TFree(send_response_buf, HYPRE_MEMORY_HOST);
459 hypre_TFree(contact_ptrs, HYPRE_MEMORY_HOST);
460 hypre_TFree(response_ptrs, HYPRE_MEMORY_HOST);
461
462 /*-----------------POST PROCESSING------------------------------*/
463
464 /* more data to receive? */
465 /* move to recv buffer and update response_recv_buf_starts */
466
467 total_size = 0; /*total number of items in response buffer */
468 num_post_recvs = 0; /*num of post processing recvs to post */
469 start_ptr = initial_recv_buf;
470 response_recv_buf_starts[0] = 0; /*already allocated above */
471
472 /*an extra loop to determine sizes. This is better than reallocating
473 the array that will be used in posting the irecvs */
474 for (i=0; i< num_contacts; i++)
475 {
476 int_ptr = (HYPRE_Int *) ((char *) start_ptr + max_response_size_bytes); /*the overhead HYPRE_Int*/
477
478 response_message_size = *int_ptr;
479 response_recv_buf_starts[i+1] =
480 response_recv_buf_starts[i] + response_message_size;
481 total_size += response_message_size;
482 if (max_response_size < response_message_size) num_post_recvs++;
483 /* start_ptr += max_response_total_bytes; */
484 start_ptr = (void *) ((char *) start_ptr + max_response_total_bytes);
485 }
486
487 post_recv_requests = hypre_TAlloc(hypre_MPI_Request, num_post_recvs, HYPRE_MEMORY_HOST);
488 post_recv_statuses = hypre_TAlloc(hypre_MPI_Status, num_post_recvs, HYPRE_MEMORY_HOST);
489 post_ptrs = hypre_TAlloc(void *, num_post_recvs, HYPRE_MEMORY_HOST);
490
491 /*second loop to post any recvs and set up recv_response_buf */
492 response_recv_buf = hypre_TAlloc(char, total_size*response_obj_size, HYPRE_MEMORY_HOST);
493 index_ptr = response_recv_buf;
494 start_ptr = initial_recv_buf;
495 count = 0;
496
497 for (i=0; i< num_contacts; i++)
498 {
499 response_message_size =
500 response_recv_buf_starts[i+1] - response_recv_buf_starts[i];
501 copy_size = hypre_min(response_message_size, max_response_size);
502
503 hypre_TMemcpy(index_ptr, start_ptr, char, copy_size*response_obj_size, HYPRE_MEMORY_HOST, HYPRE_MEMORY_HOST);
504 /* index_ptr += copy_size*response_obj_size; */
505 index_ptr = (void *) ((char *) index_ptr + copy_size*response_obj_size);
506
507 if (max_response_size < response_message_size)
508 {
509 size = (response_message_size - max_response_size)*response_obj_size;
510 post_ptrs[count] = index_ptr;
511 hypre_MPI_Irecv(post_ptrs[count], size, hypre_MPI_BYTE,
512 contact_proc_list[i], post_tag,
513 comm, &post_recv_requests[count]);
514 count++;
515 /* index_ptr+=size;*/
516 index_ptr= (void *) ((char *) index_ptr + size);
517 }
518
519 /* start_ptr += max_response_total_bytes; */
520 start_ptr = (void *) ((char *) start_ptr + max_response_total_bytes);
521 }
522
523 post_send_statuses = hypre_TAlloc(hypre_MPI_Status, post_array_size, HYPRE_MEMORY_HOST);
524
525 /*--------------CLEAN UP------------------- */
526
527 hypre_TFree(initial_recv_buf, HYPRE_MEMORY_HOST);
528
529 if (num_contacts > 0 )
530 {
531 /*these should be done */
532 hypre_MPI_Waitall(num_contacts, contact_requests, contact_statuses);
533
534 hypre_TFree(response_requests, HYPRE_MEMORY_HOST);
535 hypre_TFree(response_statuses, HYPRE_MEMORY_HOST);
536 hypre_TFree(contact_requests, HYPRE_MEMORY_HOST);
537 hypre_TFree(contact_statuses, HYPRE_MEMORY_HOST);
538 }
539
540 /* clean up from the post processing - the arrays, requests, etc. */
541
542 if (num_post_recvs)
543 {
544 hypre_MPI_Waitall(num_post_recvs, post_recv_requests, post_recv_statuses);
545 hypre_TFree(post_recv_requests, HYPRE_MEMORY_HOST);
546 hypre_TFree(post_recv_statuses, HYPRE_MEMORY_HOST);
547 hypre_TFree(post_ptrs, HYPRE_MEMORY_HOST);
548 }
549
550 if (post_array_size)
551 {
552 hypre_MPI_Waitall(post_array_size, post_send_requests, post_send_statuses);
553
554 hypre_TFree(post_send_requests, HYPRE_MEMORY_HOST);
555 hypre_TFree(post_send_statuses, HYPRE_MEMORY_HOST);
556
557 for (i=0; i< post_array_size; i++)
558 {
559 hypre_TFree(post_array[i], HYPRE_MEMORY_HOST);
560 }
561 hypre_TFree(post_array, HYPRE_MEMORY_HOST);
562 }
563
564 if (num_procs > 1)
565 {
566 hypre_TFree(term_requests, HYPRE_MEMORY_HOST);
567 hypre_TFree(term_statuses, HYPRE_MEMORY_HOST);
568
569 hypre_DestroyBinaryTree(&tree);
570 }
571
572 /* output */
573 *p_response_recv_buf = response_recv_buf;
574 *p_response_recv_buf_starts = response_recv_buf_starts;
575
576 return hypre_error_flag;
577 }
578