Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Zoltan2_Directory_Impl.hpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Zoltan2: A package of combinatorial algorithms for scientific computing
4 //
5 // Copyright 2012 NTESS and the Zoltan2 contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #ifndef ZOLTAN2_DIRECTORY_IMPL_H_
11 #define ZOLTAN2_DIRECTORY_IMPL_H_
12 
13 #include "Zoltan2_Directory.hpp"
15 
16 namespace Zoltan2 {
17 
18 // These macros were rolled over from the original zoltan code. I've preserved
19 // them for now until we have further discussion on how we want debug levels
20 // and logging to work in the new code. This should just be debug related and
21 // not impact the behavior of the code.
22 #define ZOLTAN2_PRINT_INFO(proc,yo,str) \
23  printf("ZOLTAN2 (Processor %d) %s: %s\n", (proc), (yo), \
24  ((str) != NULL ? (char *)(str) : " "));
25 
26 #define ZOLTAN2_TRACE(proc,where,yo,str) \
27  printf("ZOLTAN (Processor %d) %s %s %s\n", (proc), (where), (yo), \
28  ((str) != NULL ? (char *)(str) : " "));
29 
30 #define ZOLTAN2_TRACE_IN(proc,yo,str) \
31  ZOLTAN2_TRACE((proc),"Entering",(yo),(str));
32 
33 #define ZOLTAN2_TRACE_OUT(proc,yo,str) \
34  ZOLTAN2_TRACE((proc),"Exiting",(yo),(str));
35 
36 // Tags for MPI communications. These need unique values. Arbitrary
37 // These were rolled over from the original zoltan code and still used.
38 #define ZOLTAN2_DD_FIND_MSG_TAG 29137 /* needs 3 consecutive values */
39 #define ZOLTAN2_DD_UPDATE_MSG_TAG 29140 /* needs 2 consecutive values */
40 #define ZOLTAN2_DD_REMOVE_MSG_TAG 29142 /* needs 2 consecutive values */
41 #define ZOLTAN2_DD_RESIZE_MSG_TAG 29150 /* */
42 
43 template <typename gid_t,typename lid_t,typename user_t>
45 {
46  const char * yo = "Zoltan2_Directory::allocate";
47 
48  if (debug_level > 4) {
49  ZOLTAN2_TRACE_IN (comm->getRank(), yo, NULL);
50  }
51 
52  /* insure all processors are using the same GID, LID, USER lengths */
53  size_t array[3], max_array[3], min_array[3];
54  array[0] = sizeof(lid_t);
55  array[1] = sizeof(gid_t);
56  array[2] = sizeof(user_t);
57  Teuchos::reduceAll<int,size_t>(
58  *comm, Teuchos::REDUCE_MAX, 3, array, max_array);
59  Teuchos::reduceAll<int,size_t>(
60  *comm, Teuchos::REDUCE_MIN, 3, array, min_array);
61  if (max_array[0] != min_array[0] || max_array[1] != min_array[1]
62  || max_array[2] != min_array[2]) {
63  throw std::invalid_argument(
64  "Zoltan2_Directory() LID, GID, USER data lengths differ globally");
65  }
66 
67  // get the base size of the user data component
68  // for simple mode, this is going to just be the sizeof(user_t)
69  // for vector mode, this is sizeof(size_t) for the std::vector length
70  // the actual data will be variable
71  size_t user_base_size = is_Zoltan2_Directory_Vector() ? sizeof(size_t) :
72  size_of_value_type();
73 
74  // set up the base size to include the gid and the lid (if used) + user size
75  size_t size = sizeof(gid_t) + (use_lid?sizeof(lid_t):0) + user_base_size;
76 
77  // now calculate the full update msg size
78  update_msg_size = size + sizeof(Zoltan2_DD_Update_Msg<gid_t,lid_t>);
79 
80  // for remove message we just need the gid, not any other info
81  size = sizeof(gid_t);
82  remove_msg_size = size + sizeof(Zoltan2_DD_Remove_Msg<gid_t,lid_t>);
83 
84  // Current form of find_local is passed so gid_t is the input and
85  // lid_t is the output so this guarantees that ptr is sufficient to
86  // cover both (uses the max). This handling is consistent with the original
87  // zoltan code but I initially found it very confusing.
88  // I suggest searching for all of the places where max_id_size is used and
89  // that represents the relevant code. This may be the most optimal way of
90  // doing this. I did not get to assessing it in detail.
91  //
92  // NOTE: To really test this thoroughly we would want to play around with
93  // the values GID_SET_LENGTH and LID_SET_LENGTH in the directoryTest_Impl.hpp
94  // setup. I made sure this works for gid larger than lid and the opposite.
95  // Probably should extend the unit tests to cover all these cases at once as
96  // originally I had some bugs where this would work fine except when lid
97  // size was greater than gid size. Now this all should be ok.
98  max_id_size = std::max(sizeof(gid_t),sizeof(lid_t));
99 
100  size = max_id_size + user_base_size;
101  find_msg_size = size + sizeof(Zoltan2_DD_Find_Msg<gid_t,lid_t>);
102 
103  // TODO: Alignment is currently off for all of the modes for performance
104  // comparisons. I was not sure yet the implications of this and how it would
105  // best integrate if we have variable length user data
106 /*
107  update_msg_size = align_size_t(update_msg_size);
108  remove_msg_size = align_size_t(remove_msg_size);
109  find_msg_size = align_size_t(find_msg_size);
110 */
111 
112  if (debug_level > 4) {
113  ZOLTAN2_TRACE_OUT (comm->getRank(), yo, NULL);
114  }
115 }
116 
117 // Copy Block
118 template <typename gid_t,typename lid_t,typename user_t>
121  node_map = src.node_map;
122  return 0;
123 }
124 
125 template <typename gid_t,typename lid_t,typename user_t>
127  size_t count, const gid_t * gid, const lid_t * lid,
128  const user_t * user, const int * partition,
129  Update_Mode update_mode)
130 {
131  // for conveniece store but maybe this should be a construct property
132  // should a directory allow mixed modes (Replace, then Aggregate... for ex)
133  this->mode = update_mode;
134 
135  const char * yo = "Zoltan2_Directory::update";
136 
137  if (debug_level > 4) {
138  ZOLTAN2_TRACE_IN(comm->getRank(), yo, NULL);
139  }
140 
141  // part of initializing the error checking process
142  // for each linked list head, walk its list resetting errcheck
143  if(debug_level) {
144  for(size_t n = 0; n < node_map.size(); ++n) {
145  node_map.value_at(n).errcheck = -1; // not possible processor
146  }
147  }
148 
149  if (debug_level > 6) {
150  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After reset errcheck");
151  }
152 
153  int err = 0;
154 
155  // allocate memory for list of processors to contact
156  Teuchos::ArrayRCP<int> procs;
157  if(count > 0) {
158  procs = Teuchos::arcp(new int[count], 0, count, true);
159  }
160 
161  // set up the msg sizes for vector mode only
162  Teuchos::ArrayRCP<int> msg_sizes;
163  int sum_msg_size = 0;
164  if(is_Zoltan2_Directory_Vector() && count > 0) {
165  msg_sizes = Teuchos::arcp(new int[count], 0, count, true);
166  for (size_t i = 0; i < count; i++) {
167  size_t msg_size = get_update_msg_size(user[i]);
168  sum_msg_size += msg_size;
169  msg_sizes[i] = msg_size;
170  }
171  }
172  else {
173  sum_msg_size = update_msg_size * count; // simple case
174  }
175 
176  Teuchos::ArrayRCP<char> sbuff;
177  if(sum_msg_size) {
178  sbuff = Teuchos::arcp(new char[sum_msg_size], 0, sum_msg_size, true);
179  }
180 
182 
183  // build update messages
184  int track_offset = 0;
185  char * trackptr = sbuff.getRawPtr();
186  for (size_t i = 0; i < count; i++) {
187  // hash the gid to determine which proc will own it
188  procs[i] = hash_proc(gid[i]);
189 
190  // this ptr is the beginning of the message which can be different
191  // lengths for different gids if using Zoltan2_Directory_Vector
192  msg_t *ptr = reinterpret_cast<msg_t*>(trackptr);
193 
194  // write in all my info
195  ptr->lid_flag = lid ? 1 : 0;
196  ptr->user_flag = user ? 1 : 0;
197  ptr->partition_flag = partition ? 1 : 0;
198  ptr->partition = partition ? partition[i] : -1;
199  ptr->owner = comm->getRank();
200 
201  // now deal with the gid
202  gid_t * pgid = ptr->adjData;
203  *pgid = gid[i];
204 
205  // optionally write the lid if we are using that
206  if(use_lid) {
207  if(!lid) {
208  throw std::logic_error(
209  "Did not pass lid values but directory was created to use them!");
210  }
211  lid_t * plid = reinterpret_cast<lid_t*>(ptr->adjData + 1);
212  *plid = lid[i];
213  }
214  else {
215  if(lid) {
216  throw std::logic_error(
217  "Passed lid values but directory was created not to use them!");
218  }
219  }
220 
221  // find the spot where the user data begins
222  user_t * puser = reinterpret_cast<user_t*>(
223  reinterpret_cast<char*>(ptr->adjData) + sizeof(gid_t) +
224  (use_lid?sizeof(lid_t):0));
225 
226  // write in the user data - for Zoltan2_Directory_Simple this is a trival
227  // copy but for Zoltan2_Directory_Vector we write length and then write all
228  // of the elements in the vector.
229  if (user) {
230  user_to_raw(user[i], puser);
231  }
232  else {
233  // The update msg contains space for the vector length (sizeof(size_t))
234  // if it's a Zoltan2_Directory_Vector
235  *puser = user_t(); // create an empty result
236  }
237 
238  // vector will have different lengths but the simple mode will have all
239  // lengths just update_msg_size
240  size_t new_update_msg_size =
241  is_Zoltan2_Directory_Vector() ? msg_sizes[i] : update_msg_size;
242  track_offset += new_update_msg_size;
243  trackptr += new_update_msg_size;
244  }
245 
246  // this check just makes sure our internal logic above is correct
247  // we should have looped to total size sum_msg_size
248  if(track_offset != sum_msg_size) {
249  throw std::logic_error("Bad summing!");
250  }
251 
252  if (debug_level > 6) {
253  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After fill contact list");
254  }
255 
256  // now create efficient communication plan
257 
258  // Kokkos mode uses the new refactored C++ communicator class
259  Zoltan2_Directory_Comm directoryComm(count, procs, comm,
261  int nrec = directoryComm.getNRec();
262 
263  if (err) {
264  throw std::logic_error("Zoltan2_Directory::update() Comm_Create error");
265  }
266 
267  if (debug_level > 6) {
268  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After Comm_Create");
269  }
270 
271  int sum_recv_sizes = 0;
272  if(is_Zoltan2_Directory_Vector()) {
273  // Only vector mode has to use the resizing options for getting receive info
274  err = directoryComm.resize(msg_sizes,
275  ZOLTAN2_DD_RESIZE_MSG_TAG, &sum_recv_sizes);
276  }
277  else {
278  sum_recv_sizes = update_msg_size * nrec;
279  }
280 
281  if (err) {
282  throw std::logic_error("directoryComm.execute_resize error");
283  }
284 
285  // If dd has no nodes allocated (e.g., first call to DD_Update;
286  // create the nodelist and freelist
287 
288  // TODO upgrade nrec as size_t and all corresponding changes...
289  if(nrec && static_cast<int>(node_map.size()) < nrec) {
290  // some things to consider here if we will have multiple update calls in
291  // series... how will subsequent calls optimally manage this list since the
292  // new gid set may be partially overlapped with the original. Currently the
293  // update_local has a mechanism to rehash and increase this size if we run
294  // out so skipping this call would be logically ok (but not best performance)
295  rehash_node_map(nrec);
296  }
297 
298  // allocate receive buffer for nrec DD_Update_Msg structures
299  Teuchos::ArrayRCP<char> rbuff;
300  if(sum_recv_sizes > 0) {
301  rbuff = Teuchos::arcp(
302  new char[sum_recv_sizes], 0, sum_recv_sizes, true); // receive buffer
303  }
304 
305  // send my update messages & receive updates directed to me
306  //if resizing we send size 1 because the sizes will be built individually
307  const int nbytes = is_Zoltan2_Directory_Vector() ? 1 : update_msg_size;
308 
309  err = directoryComm.do_forward(ZOLTAN2_DD_UPDATE_MSG_TAG+1,
310  sbuff, nbytes, rbuff);
311 
312  if (err) {
313  throw std::logic_error("Zoltan2_Directory::update() Comm_Do error");
314  }
315 
316  if (debug_level > 6) {
317  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After Comm_Do");
318  }
319 
320  int errcount = 0;
321 
322  // for each message rec'd, update local directory information
323  track_offset = 0;
324  trackptr = rbuff.getRawPtr();
325  for (int i = 0; i < nrec; i++) {
326  msg_t *ptr = reinterpret_cast<msg_t*>(trackptr);
327 
328  user_t * puser = (ptr->user_flag) ?
329  (user_t*)(reinterpret_cast<char*>(ptr->adjData) +
330  sizeof(gid_t) + (use_lid?sizeof(lid_t):0)) : NULL;
331 
332  err = update_local(ptr->adjData,
333  (ptr->lid_flag) ? reinterpret_cast<lid_t*>(ptr->adjData + 1) : NULL,
334  puser,
335  (ptr->partition_flag) ? (ptr->partition) : -1, // illegal partition
336  ptr->owner);
337 
338  if (err)
339  ++errcount;
340 
341  // in this case we are reading the raw data so we calculate the message
342  // size from it. For vector mode, this will find the length of the vector
343  // and calculate the full message size from that.
344  size_t delta_msg_size = get_update_msg_size(puser);
345  trackptr += delta_msg_size;
346  track_offset += delta_msg_size;
347  }
348 
349  // safety check - if all this internal logic is correct the ptr offset should
350  // not be exactly at the end of the recv buffer.
351  if(track_offset != sum_recv_sizes) {
352  throw std::logic_error("Did not sum!");
353  }
354 
355  if (debug_level > 6) {
356  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After Local update");
357  }
358 
359  err = 0;
360 
361  if (debug_level) { // overwrite error return if extra checking is on
362  err = (errcount) ? 1 : 0;
363  }
364 
365  if (debug_level) {
366  char str[100]; // used to build message string
367  sprintf (str, "Processed %lu GIDs (%d local), %d GID errors", count,
368  directoryComm.getNRec(),
369  errcount);
370  ZOLTAN2_PRINT_INFO (comm->getRank(), yo, str);
371  }
372 
373  if (debug_level > 4) {
374  ZOLTAN2_TRACE_OUT(comm->getRank(), yo, NULL);
375  }
376 
377  return err;
378 }
379 
380 template <typename gid_t,typename lid_t,typename user_t>
382  gid_t* gid, /* GID to update (in) */
383  lid_t* lid, /* gid's LID (in), NULL if not needed */
384  user_t *user, /* gid's user data (in), NULL if not needed */
385  int partition, /* gid's partition (in), -1 if not used */
386  int owner) /* gid's current owner (proc number) (in) */
387 {
388  const char * yo = "Zoltan2_Directory::update_local";
389 
390  // input sanity checking
391  if (owner < 0) {
392  throw std::logic_error(
393  "Zoltan2_Directory::update_local() owner < 0");
394  }
395  if (owner >= comm->getSize()) {
396  throw std::logic_error(
397  "Zoltan2_Directory::update_local() owner >= comm->getSize()");
398  }
399  if (gid == NULL) {
400  throw std::logic_error(
401  "Zoltan2_Directory::update_local() gid == NULL");
402  }
403 
404  if (debug_level > 5) {
405  ZOLTAN2_TRACE_IN (comm->getRank(), yo, NULL);
406  }
407 
408  // compute offset into hash table to find head of linked list
409  size_t node_index = node_map.find(*gid);
410  if(node_map.valid_at(node_index)) {
412  node_map.value_at(node_index);
413 
414  // found match, update directory information
415  if (lid) {
416  node.lid = *lid;
417  }
418  if (user) {
419  update_local_user(user, node.userData);
420  }
421 
422  node.owner = owner;
423  if (partition != -1) {
424  node.partition = partition;
425  }
426 
427  // errcheck is reset to -1 at beginning of update for debug mode
428  // then it will get set to the provider of the data when the node is
429  // created or on the first call to be updated locally.
430  // So if errcheck -1, then we do nothing - it's the first action.
431  if(debug_level) {
432  // if node.errcheck is -1 we are detecting the first update to a node
433  // which already existed at the beginning of the update call so it's ok.
434  // if mode is not Replace then duplicate calls are ok (expected) since
435  // Add and Aggregate combine from many procs are the outcome is not
436  // order dependent.
437  if(node.errcheck != -1 && mode == Replace) {
438  // here we have detected two actions on a single node in the same
439  // update call for replace.
440  // The actions could have been: create node -> change node
441  // or if the node already existed: change node -> change node
442 
443  // in Replace mode, two actions are potentially problematic.
444  // If two procs update the same gid with different data it will
445  // be order dependent.
446 
447  // For performance testing it's convenient to allow
448  // Replace to be called from different procs but expect the data
449  // to always be identical. That means we can compare Replace and
450  // Aggregate more meaningfully since the gid setup for update is
451  // the same.
452 
453  // To discuss - should one proc be allowed to submit duplicate
454  // gids in an update call using Replace mode. Should two different
455  // procs be allowed to submit duplicate gids with the same data
456  // for a replace call, in which case the outcome is determined
457  // regardless of order but we would be relying on the user to have
458  // accurate data submission. Then we might consider a debug check
459  // here to validate the data is coming in matches the local data.
460 
461  // TODO: Probably add this throw in, but currently the testing
462  // framework will feed multiple Replace calls with the same data
463  // from different gids - so we would have to change the tests
464  // to guarantee Replace was a unique gid lists for each proc.
465  // That is easy to do but not ideal at the moment for performance
466  // testing reasons. I'd like the pattern of calls to be the same
467  // as Add and Aggregate as a reference.
468 
469  // throw std::logic_error( "Two replace calls were detected on."
470  // " the same gid which can be an undefined results.");
471  }
472  }
473 
474  node.errcheck = owner;
475 
476  if (debug_level > 5) {
477  ZOLTAN2_TRACE_OUT (comm->getRank(), yo, NULL);
478  }
479 
480  // TODO: Change logic flow to avoid this internal return
481  return 0; // ignore all errors
482  }
483 
484  // gid not found.
485  // Create new Zoltan2_Directory_Node<gid_t,lid_t> and fill it in
486  Zoltan2_Directory_Node<gid_t,lid_t,user_t> node; // will add to hash at end
487  node.free = 0; // TODO - is this necessary - see notes in rehash_node_map
488  node.lid = lid ? (*lid) : lid_t();
489 
490  // this is more or less doing what above relic commands did except for
491  // vector mode there is special handling to account for the variable length
492  // of the std::vector
493  if(user) {
494  raw_to_user(user, node.userData);
495  }
496  else {
497  node.userData = user_t();
498  }
499 
500  node.partition = partition;
501  node.owner = owner;
502  node.errcheck = owner;
503 
504  if(node_map.insert(*gid, node).failed()) {
505  // Need more nodes (that's the assumption here if we have failure)
506  // A new update has added more to our local list and we need more capacity.
507  // TODO: Decide most efficient scheme. Here we bump to at least 10 or if
508  // we're already at the level, increase by 10% increments. I think this will
509  // be less efficient for small scale problems, when we probably care less,
510  // but more efficient as we scale up.
511  size_t new_guess_size = (node_map.size() < 10) ? 10 :
512  ( node_map.size() + node_map.size()/10); // adds a minimum of 1
513  rehash_node_map(new_guess_size);
514  if(node_map.insert(*gid, node).failed()) {
515  throw std::logic_error("Hash insert failed. Mem sufficient?....");
516  }
517  }
518 
519  if (debug_level > 6) {
520  ZOLTAN2_PRINT_INFO (comm->getRank(), yo, "Created new directory item");
521  }
522 
523  if (debug_level > 5) {
524  ZOLTAN2_TRACE_OUT (comm->getRank(), yo, NULL);
525  }
526 
527  return 0;
528 }
529 
530 template <typename gid_t,typename lid_t,typename user_t>
532  size_t count,
533  const gid_t * gid, /* Incoming list of GIDs to get owners proc */
534  lid_t * lid, /* Outgoing corresponding list of LIDs */
535  user_t * user, /* Outgoing optional corresponding user data */
536  int * partition, /* Outgoing optional partition information */
537  int * owner, /* Outgoing optional list of data owners */
538  bool throw_if_missing) /* default true - throw if gid is not found. */
539 {
540  const char * yo = "Zoltan2_Directory::find";
541 
542  if (debug_level > 4) {
543  ZOLTAN2_TRACE_IN(comm->getRank(), yo, NULL);
544  }
545 
546  int err = 0;
547 
548  /* allocate memory for processors to contact for directory info */
549  Teuchos::ArrayRCP<int> procs;
550  if(count > 0) {
551  procs = Teuchos::arcp(
552  new int[count], 0, count, true); // processors to contact
553  }
554 
555  /* Setup procs list */
556  for (size_t i = 0; i < count; i++) {
557  procs[i] = hash_proc(gid[i]); // determines the owner
558  }
559 
560  // create efficient communication plan
561  Zoltan2_Directory_Comm directoryComm(count, procs, comm,
563  int nrec = directoryComm.getNRec();
564 
565  if (debug_level > 6) {
566  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After Comm_Create");
567  }
568 
569  if (err) {
570  throw std::logic_error("Zoltan2_Directory::find() error");
571  }
572 
573  Teuchos::ArrayRCP<char> sbuff;
574  if(count > 0) {
575  sbuff = Teuchos::arcp(new char[find_msg_size*count],
576  0, find_msg_size*count, true); // send buffer
577  }
578 
579  typedef Zoltan2_DD_Find_Msg<gid_t,lid_t> msg_t;
580 
581  /* for each GID, fill DD_Find_Msg buffer and contact list */
582  char *trackptr = sbuff.getRawPtr();
583  for (size_t i = 0; i < count; i++) {
584  msg_t *ptr = reinterpret_cast<msg_t*>(trackptr);
585  ptr->index = i;
586  ptr->proc = procs[i];
587  *(ptr->adjData) = gid[i];
588  trackptr += find_msg_size;
589  }
590 
591  if (debug_level > 6) {
592  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After fill");
593  }
594 
595  if (err) {
596  throw std::logic_error("directoryComm.execute_resize error");
597  }
598 
599  // allocate receive buffer
600  Teuchos::ArrayRCP<char> rbuff;
601  if(nrec > 0) {
602  rbuff = Teuchos::arcp(new char[nrec * find_msg_size],
603  0, nrec * find_msg_size, true);
604  }
605 
606  const int nbytes = find_msg_size; // just getting length not full vector data
607 
608  err = directoryComm.do_forward(ZOLTAN2_DD_FIND_MSG_TAG+1, sbuff,
609  nbytes, rbuff);
610 
611  if (err) {
612  throw std::logic_error("Zoltan2_Directory::find() error");
613  }
614 
615  if (debug_level > 6) {
616  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After Comm_Do");
617  }
618 
619  // get find messages directed to me and determine total recv size
620  // and a list of individual sizes (rmsg_sizes_resized)
621  Teuchos::ArrayRCP<int> rmsg_sizes_resized;
622  if(nrec > 0) {
623  rmsg_sizes_resized = Teuchos::arcp(new int[nrec], 0, nrec, true);
624  }
625  Teuchos::ArrayRCP<int>::size_type sum_rmsg_sizes_resized = 0;
626 
627  char *track_ptr = rbuff.getRawPtr();
628  for (int i = 0; i < nrec; i++) {
629  msg_t *msg = reinterpret_cast<msg_t*>(track_ptr);
630  track_ptr += find_msg_size;
631  size_t find_rmsg_size_resized = get_local_find_msg_size(msg->adjData,
632  throw_if_missing);
633  rmsg_sizes_resized[i] = find_rmsg_size_resized;
634  sum_rmsg_sizes_resized += find_rmsg_size_resized;
635  }
636 
637  Teuchos::ArrayRCP<char>::size_type track_offset_resized = 0;
638 
639  Teuchos::ArrayRCP<char> rbuff_resized_build;
640 
641  if(is_Zoltan2_Directory_Vector()) {
642 
643  if(sum_rmsg_sizes_resized > 0) {
644  rbuff_resized_build = Teuchos::arcp(new char[sum_rmsg_sizes_resized],
645  0, sum_rmsg_sizes_resized, true);
646  }
647 
648  track_ptr = rbuff.getRawPtr();
649  char * track_ptr_resized = rbuff_resized_build.getRawPtr();
650  for (int i = 0; i < nrec; i++) {
651  memcpy(track_ptr_resized, track_ptr, find_msg_size);
652  track_ptr += find_msg_size;
653  track_ptr_resized += rmsg_sizes_resized[i];
654  }
655  }
656 
657  // for performance, when not using variable sized we can optimize this step
658  // and use the original rbuff (there is no resizing to consider)
659  Teuchos::ArrayRCP<char> rbuff_resized = is_Zoltan2_Directory_Vector() ?
660  rbuff_resized_build : rbuff;
661 
662  // Fill it with the true array data
663  track_offset_resized = 0; // for debugging
664  track_ptr = rbuff_resized.getRawPtr();
665  for (int i = 0; i < nrec; i++) {
666  typedef Zoltan2_DD_Find_Msg<gid_t,lid_t> find_msg_t;
667  find_msg_t *ptr = reinterpret_cast<find_msg_t*>(track_ptr);
668  user_t * puser = reinterpret_cast<user_t*>(
669  reinterpret_cast<char*>(ptr->adjData) + max_id_size);
670 
671  // In original DD_Find_Local the first two values (gid and lid) are
672  // passed as the same, we send in gid and collect lid if it's used.
673  // that is why the find_msg_size is setup originally using max of
674  // sizeof(gid_t) and sizeof(lid_t)
675  err = find_local(ptr->adjData, (lid_t*)ptr->adjData,
676  puser, &ptr->partition, &ptr->proc, throw_if_missing);
677 
678  const size_t & size_shift = rmsg_sizes_resized[i];
679  track_offset_resized += size_shift;
680  track_ptr += size_shift;
681  }
682 
683  if(track_offset_resized != sum_rmsg_sizes_resized) {
684  throw std::logic_error("Bad sum!");
685  }
686 
687  // This section is handled differently if it's variable sized array
688  size_t size_scale = is_Zoltan2_Directory_Vector() ? 1 : find_msg_size;
689 
690  Teuchos::ArrayRCP<char> sbuff_resized;
691  if(!is_Zoltan2_Directory_Vector()) {
692  sbuff_resized = sbuff; // vector mode will set this and fill it
693  }
694 
695  if(is_Zoltan2_Directory_Vector()) {
696  err = directoryComm.do_reverse(ZOLTAN2_DD_FIND_MSG_TAG+2,
697  rbuff_resized, size_scale, rmsg_sizes_resized, sbuff_resized);
698  }
699  else {
700  err = directoryComm.do_reverse(ZOLTAN2_DD_FIND_MSG_TAG+2,
701  rbuff_resized, size_scale, Teuchos::null, sbuff_resized);
702  }
703 
704  if (err) {
705  throw std::logic_error("Zoltan2_Directory::find() do reverse failed");
706  }
707 
708  if (debug_level > 6) {
709  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After Comm_Reverse");
710  }
711 
712  // fill in user supplied lists with returned information
713  track_offset_resized = 0;
714 
715  // it's not going to be in order... so don't use gid[i]
716  char * trackptr_resized = sbuff_resized.getRawPtr();
717  for (size_t i = 0; i < count; i++) {
718 
719  if(track_offset_resized >= sbuff_resized.size()) {
720  printf(
721  "%d has gid.size() %d track_offset_resized: %d sbuff_resized: %d\n",
722  comm->getRank(),
723  (int) count, (int) track_offset_resized, (int) sbuff_resized.size());
724  throw std::logic_error("Bad buffer overflow! Internal error.");
725  }
726 
727  msg_t *ptr = reinterpret_cast<msg_t*>(trackptr_resized);
728 
729  if (owner)
730  owner[ptr->index] = ptr->proc;
731  if (partition)
732  partition[ptr->index] = ptr->partition ;
733  if (lid) {
734  memcpy (&lid[ptr->index], ptr->adjData, sizeof(lid_t));
735  }
736 
737  user_t * pRead = reinterpret_cast<user_t*>(
738  reinterpret_cast<char*>(ptr->adjData) + max_id_size);
739 
740  // if find_local failed proc is set to -1. Then we can leave the data
741  // untouched - the default behavior is to throw but the unit tests are
742  // set up to track and verify each remove id was properly taken out. To do
743  // this the test overrides with an optional flag on find() and says do not
744  // throw - and expects the data to remain untouched - then validates the
745  // data is not changed.
746  if(ptr->proc != -1 && user) {
747  raw_to_user(pRead, user[ptr->index]);
748  }
749 
750  // don't use smsg_sizes_resized here because we don't get them back
751  // in the same order
752  size_t incoming_size = get_incoming_find_msg_size(ptr);
753  trackptr_resized += incoming_size;
754  track_offset_resized += incoming_size;
755  }
756 
757  if(track_offset_resized != sbuff_resized.size()) {
758  throw std::logic_error("Bad buffer sum!");
759  }
760 
761  if (debug_level > 6) {
762  ZOLTAN2_PRINT_INFO(comm->getRank(), yo, "After fill return lists");
763  }
764 
765  int errcount = 0;
766  Teuchos::reduceAll<int>(*comm, Teuchos::REDUCE_SUM, 1, &errcount, &err);
767  err = (err) ? 1 : 0;
768 
769  // if at least one GID was not found, potentially notify caller of error
770  if (debug_level > 0) {
771  char str[100]; /* diagnostic message string */
772  sprintf(str, "Processed %lu GIDs, GIDs not found: %d", count, errcount);
773  ZOLTAN2_PRINT_INFO (comm->getRank(), yo, str);
774  }
775 
776  if (debug_level > 4) {
777  ZOLTAN2_TRACE_OUT(comm->getRank(), yo, NULL);
778  }
779 
780  return err;
781 }
782 
783 template <typename gid_t,typename lid_t,typename user_t>
785  gid_t* gid, /* incoming GID to locate (in) */
786  lid_t* lid, /* gid's LID (out) */
787  user_t *user, /* gid's user data (out) */
788  int *partition, /* gid's partition number (out) */
789  int *owner, /* gid's owner (processor number) (out) */
790  bool throw_if_missing) const
791 {
792  const char * yo = "Zoltan2_Directory::find_local";
793 
794  /* input sanity check */
795  if (owner == NULL || gid == NULL) {
796  throw std::logic_error("Zoltan2_Directory::find_local() Invalid input");
797  }
798 
799  if (debug_level > 5) {
800  ZOLTAN2_TRACE_IN (comm->getRank(), yo, NULL);
801  }
802 
803  /* compute offset into hash table to find head of linked list */
804  // Note performance is better if we first get index, then check valid_at,
805  // and use index to call value_at. Alternative is to call exists(*gid) and
806  // then node_map.value_at(node_map.find(*gid))) which is slower.
807  // TODO: Can this be optimized further?
808  size_t node_index = node_map.find(*gid);
809  if(node_map.valid_at(node_index))
810  {
812  node_map.value_at(node_index);
813  /* matching global ID found! Return gid's information */
814  if(lid) {
815  *lid = node.lid;
816  }
817 
818  if (user) {
819  user_to_raw(node.userData, user);
820  }
821 
822  if (owner) *owner = node.owner;
823  if (partition) *partition = node.partition;
824 
825  if (debug_level > 5) {
826  ZOLTAN2_TRACE_OUT (comm->getRank(), yo, NULL);
827  }
828  return 0; // success point
829  }
830 
831  // failure point
832  if (owner != NULL)
833  *owner = -1; /* JDT Added -1 owner not found */
834 
835  if (debug_level > 5) {
836  ZOLTAN2_TRACE_OUT (comm->getRank(), yo, NULL);
837  }
838 
839  if(throw_if_missing) {
840  // TODO: This used to be linked to debug_level but wanted to discuss as it
841  // seems we would want an error always if this failed.
842  throw std::logic_error("find_local did not succeed");
843  }
844 
845  return 0;
846 }
847 
848 // Print block
849 template <typename gid_t,typename lid_t,typename user_t>
851 {
852  const char * yo = "Zoltan2_Directory::print";
853 
854  if (debug_level > 4) {
855  ZOLTAN2_TRACE_IN (comm->getRank(), yo, NULL);
856  }
857 
858  for (size_t i = 0; i < node_map.size(); i++) {
860  node_map.value_at(i);
861  printf ("ZOLTAN DD Print(%d): \tList, \tGID ", comm->getRank());
862  printf("(");
863  // the issue here is that gid could be a simple type (int/long) or it
864  // could be an arbitrary structure such as:
865  // struct some_type {
866  // int x[4];
867  // }
868  // Directory works for such arbitrary type but only knows sizeof,
869  // not details of the internal implementation. In the testing framework
870  // the length of above x array is stored so it can be printed.
871  // TODO: Decide how to handle this - if we want directory to be able to
872  // print nice output of the gid (and lid) then perhaps we need to require
873  // that such structs define a to_string() method.
874  printf( "TODO: Decide how to print gid of arbitrary type.");
875  printf(") ");
876  if (use_lid) {
877  printf("\tLID (");
878  // see above note on the gid and printing
879  printf( "TODO: Decide how to print lid of arbitrary type.");
880  printf(") ");
881  }
882  printf ("\tPart %d\n", node.partition);
883  printf ("\tOwner %d\n", node.owner);
884  }
885 
886  if (debug_level > 4) {
887  ZOLTAN2_TRACE_OUT (comm->getRank(), yo, NULL);
888  }
889  return 0;
890 }
891 
892 // Stats block
893 template <typename gid_t,typename lid_t,typename user_t>
895 {
896  const char *yo = "Zoltan2_Directory::stats";
897 
898  if (debug_level > 4) {
899  ZOLTAN2_TRACE_IN (comm->getRank(), yo, NULL);
900  }
901 
902  char str[100]; // used to build message string
903  // not much to do here for equivalent to stats
904  // TODO: Consider removing stats()
905  sprintf(str, "Kokkos unordered map %d nodes.", node_map.size());
906  ZOLTAN2_PRINT_INFO (comm->getRank(), yo, str);
907  if (debug_level > 4) {
908  ZOLTAN2_TRACE_OUT (comm->getRank(), yo, NULL);
909  }
910 }
911 
912 template <typename gid_t,typename lid_t,typename user_t>
914  size_t count,
915  const gid_t * gid) /* Incoming list of GIDs to remove */
916 {
917  const char * yo = "Zoltan2_Directory::remove";
918 
919  if (debug_level > 4) {
920  ZOLTAN2_TRACE_IN (comm->getRank(), yo, NULL);
921  }
922 
923  int err = 0;
924 
925  // allocate memory for processor contact list
926  Teuchos::ArrayRCP<int> procs;
927  Teuchos::ArrayRCP<char> sbuff;
928  if(count > 0) {
929  procs = Teuchos::arcp( // list of processors to contact
930  new int[count], 0, count, true);
931  sbuff = Teuchos::arcp( // send buffer
932  new char[count*remove_msg_size], 0, count*remove_msg_size, true);
933  }
934 
936 
937  // for each GID, fill in contact list and then message structure
938  char * trackptr = sbuff.getRawPtr();
939  for (size_t i = 0; i < count; i++) {
940  procs[i] = hash_proc(gid[i]);
941  msg_t *ptr = reinterpret_cast<msg_t*>(trackptr);
942  ptr->owner = comm->getRank();
943  *(ptr->adjData) = gid[i];
944  trackptr += remove_msg_size;
945  }
946 
947  // now create efficient communication plan
948  Zoltan2_Directory_Comm directoryComm(count, procs, comm,
950  int nrec = directoryComm.getNRec();
951 
952  if (err) {
953  throw std::logic_error("Zoltan_Comm_Create failed");
954  }
955 
956  if (debug_level > 6) {
957  ZOLTAN2_PRINT_INFO (comm->getRank(), yo, "After Zoltan_Comm_Create");
958  }
959 
960  // allocate receive buffer for nrec DD_Remove_Msg structures
961  Teuchos::ArrayRCP<char> rbuff;
962  if(nrec > 0) {
963  rbuff = Teuchos::arcp(new char[nrec*remove_msg_size],
964  0, nrec*remove_msg_size, true); // receive buffer
965  }
966 
967  // send my update messages & receive updates directed to me
968  err = directoryComm.do_forward(ZOLTAN2_DD_UPDATE_MSG_TAG+1,
969  sbuff, remove_msg_size, rbuff);
970 
971  if (err) {
972  throw std::logic_error("Comm_Do error");
973  }
974 
975  if (debug_level > 6) {
976  ZOLTAN2_PRINT_INFO (comm->getRank(), yo, "After Zoltan_Comm_Do");
977  }
978 
979  /* for each message rec'd, remove local directory info */
980  int errcount = 0;
981 
982  // Note calling begin_erase() and end_erase() without actually erasing
983  // something leads to confusing seg faults. Hence nrec>0 check.
984  // Pulling begin_erase and end_erase out of the loop is important for
985  // performance. Since the actual erase is fairly simple we may consider
986  // eliminating remove_local and just calling here but will keep for now to
987  // keep symmetry with other modes.
988  if(nrec > 0) {
989  node_map.begin_erase();
990  for (int i = 0; i < nrec; i++) {
991  msg_t *ptr = reinterpret_cast<msg_t*>(&(rbuff[i*remove_msg_size]));
992  err = remove_local(ptr->adjData);
993  if (err == 1) { // TODO eliminate warns (1) make all errors
994  ++errcount;
995  }
996  }
997  node_map.end_erase();
998  } // if nrec > 0
999 
1000  err = 0;
1001 
1002  if (debug_level) {
1003  char str[100]; // used to build message string
1004  sprintf (str, "Processed %zu GIDs (%d local), %d GIDs not found",
1005  count, nrec, errcount);
1006  ZOLTAN2_PRINT_INFO (comm->getRank(), yo, str);
1007  err = (errcount) ? 1 : 0;
1008  }
1009 
1010  return err;
1011 }
1012 
1013 template <typename gid_t,typename lid_t,typename user_t>
1015  gid_t* gid) /* GID to be removed (in) */
1016 {
1017  const char * yo = "Zoltan2_Directory::remove_local";
1018 
1019  /* input sanity checking */
1020  if (gid == NULL) {
1021  throw std::logic_error("Invalid input argument");
1022  }
1023 
1024  if (debug_level > 5) {
1025  ZOLTAN2_TRACE_IN (comm->getRank(), yo, NULL);
1026  }
1027 
1028  if(node_map.exists(*gid)) {
1029  node_map.erase(*gid);
1030  return 0;
1031  }
1032 
1033  /* We get here only if the global ID has not been found */
1034  if (debug_level > 5) {
1035  ZOLTAN2_TRACE_OUT (comm->getRank(), yo, NULL);
1036  }
1037 
1038  return 1;
1039 }
1040 
1041 template <typename gid_t,typename lid_t,typename user_t>
1043  const gid_t & gid) const
1044 {
1045  uint32_t k;
1046 
1047  // Copied from murmurc.3
1048  // TODO: Will be removed as Kokkos form develops
1049  #define ZOLTAN2_ROTL32(x,r) (uint32_t) \
1050  (((uint32_t)(x) << (int8_t)(r)) | ((uint32_t)(x) >> (32 - (int8_t)(r))))
1051 
1052  const void * key = (void *)(&gid);
1053  int len = sizeof(gid_t);
1054  uint32_t seed = 14;
1055  void * out = (void *)&k;
1056 
1057  const uint8_t * data = (const uint8_t*)key;
1058  const int nblocks = len / 4;
1059  int i;
1060  uint32_t h1 = seed;
1061  uint32_t c1 = 0xcc9e2d51;
1062  uint32_t c2 = 0x1b873593;
1063  const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
1064  for(i = -nblocks; i; i++)
1065  {
1066  uint32_t k1 = blocks[i];
1067  k1 *= c1;
1068  k1 = ZOLTAN2_ROTL32(k1,15);
1069  k1 *= c2;
1070  h1 ^= k1;
1071  h1 = ZOLTAN2_ROTL32(h1,13);
1072  h1 = h1*5+0xe6546b64;
1073  }
1074  const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
1075  uint32_t k1 = 0;
1076  switch(len & 3)
1077  {
1078  case 3: k1 ^= tail[2] << 16;
1079  case 2: k1 ^= tail[1] << 8;
1080  case 1: k1 ^= tail[0];
1081  k1 *= c1; k1 = ZOLTAN2_ROTL32(k1,15); k1 *= c2; h1 ^= k1;
1082  };
1083  h1 ^= len;
1084  h1 ^= h1 >> 16;
1085  h1 *= 0x85ebca6b;
1086  h1 ^= h1 >> 13;
1087  h1 *= 0xc2b2ae35;
1088  h1 ^= h1 >> 16;
1089  *(uint32_t*)out = h1;
1090 
1091  return(k % comm->getSize());
1092 }
1093 
1094 
1095 /* TODO: Currently disabled - I need to review the benefits of this and see if
1096  we need this in the new version. Also how do we best handle this for variable
1097  sized data. */
1098 /*
1099 template <typename gid_t,typename lid_t,typename user_t>
1100 size_t Zoltan2_Directory<gid_t,lid_t,user_t>::align_size_t(size_t a) const
1101 {
1102  #define ZOLTAN2_ALIGN_VAL 7U
1103  return((ZOLTAN2_ALIGN_VAL + a) & ~ZOLTAN2_ALIGN_VAL);
1104 }
1105 */
1106 
1107 } // end namespace Zoltan2
1108 
1109 #endif // ZOLTAN2_DIRECTORY_IMPL_H_
#define ZOLTAN2_DD_FIND_MSG_TAG
int find(size_t length, const gid_t *gid, lid_t *lid, user_t *user, int *partition, int *owner, bool throw_if_missing=true)
Can be Replace, Add, or Aggregate.
int update(size_t length, const gid_t *gid, const lid_t *lid, const user_t *user, const int *partition, Update_Mode update_mode)
update is called by user to submit new data.
void stats() const
stats. New Kokkos mode needs further development.
#define ZOLTAN2_DD_UPDATE_MSG_TAG
int print() const
gids to remove.
int copy(const Zoltan2_Directory< gid_t, lid_t, user_t > &dd)
int do_reverse(int tag, const Teuchos::ArrayRCP< char > &send_data, int nbytes, const Teuchos::ArrayRCP< int > &sizes, Teuchos::ArrayRCP< char > &recv_data)
int update_local(gid_t *gid, lid_t *lid, user_t *user, int partition, int owner)
int remove(size_t length, const gid_t *gid)
if true will throw if a gid is not found. This is used by the unit tests to properly assess if remove...
Update_Mode
Update_Mode determines how update executes.
unsigned int hash_proc(const gid_t &gid) const
int find_local(gid_t *gid, lid_t *lid, user_t *user, int *partition, int *owner, bool throw_if_missing=true) const
#define ZOLTAN2_ROTL32(x, r)
#define ZOLTAN2_TRACE_OUT(proc, yo, str)
int do_forward(int tag, const Teuchos::ArrayRCP< char > &send_data, int nbytes, Teuchos::ArrayRCP< char > &recv_data)
int resize(const Teuchos::ArrayRCP< int > &sizes, int tag, int *sum_recv_sizes)
#define ZOLTAN2_DD_RESIZE_MSG_TAG
#define ZOLTAN2_TRACE_IN(proc, yo, str)
Zoltan2_Directory is an abstract base class.
#define ZOLTAN2_PRINT_INFO(proc, yo, str)
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > user_t
Definition: Metric.cpp:39