Zoltan2
Loading...
Searching...
No Matches
directoryTest_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#include <Teuchos_RCP.hpp>
11#include <Teuchos_ArrayView.hpp>
12#include <Zoltan2_TPLTraits.hpp>
14
15namespace Zoltan2 {
16
17// The directory also has modes but currently working with an Original mode
18// as well, which doesn't have it - so created this to have a universal
19// reference to use. Eventually will delete all but Kokkos and then eliminate
20// this declaration.
25 TestMode_Max // exists to provide a loop through these modes
26};
27
28// Do gid with multiple sub gids
29// Note that the testing code (things in this file) needs knowledge
30// about this structure so it's a hard coded assumption but the directory
31// class itself does not - that is why there are sub_gid references in the
32// below classes but no special handling for this in the directory.
33// as long as sizeof works and it can be returned as a function
34// type it should be ok to use as a gid_t.
35#define GID_SET_LENGTH 3 // arbitrary for testing
39
40// same as gid above but this is for lid
41#define LID_SET_LENGTH 4 // just to mix things up - make it different than gid
45
46// a utility function to print messages out for each rank or optionally
47// just for rank 0. Used to prevent mangled output.
48void print_proc_safe(const std::string& message,
49 Teuchos::RCP<const Teuchos::Comm<int> > comm, bool only_rank0 = true) {
50 for(int proc = 0; proc < comm->getSize(); ++proc) {
51 comm->barrier();
52 if(proc == comm->getRank() && (!only_rank0 || proc == 0)) {
53 std::cout << message << std::endl;
54 }
55 }
56 comm->barrier();
57}
58
59// For the new code we use -1 as the 'unset' value to make it easier
60// to see it worked but it's arbitrary. TODO: Discuss error behaviors for
61// remove when not found.
62#define NOT_FOUND_VALUE -1 // easier to tell it's the unset value as -1
63
64// This class manages the IDs that we will update to the directory and which
65// ids to remove and find. The class also handles error checking at the end
66// to validate the find got the proper user data we expected.
67//
68// Derived classes handle vector user_t versus single user_t and
69// also multiple gids (gid = [1,2,8,3]) or single gid (gid = 10).
70// All this machinery is just for testing/evaluation and nothing really to
71// do with the directory class itself.
72
73// Because this testing code started to get complex due covering all possible
74// behaviors of the directory, another test was created called
75// directoryTest_KokkosSimple. That doesn't reference any of this stuff and is
76// a more natural example of the directory being used (but doesn't thoroughly
77// test the directory as is done here.
78//
79// The class structure is this:
80// IDs
81// Single_GID - code for gid is simple type
82// Multiple_GID - code for gid such as gid = [1,2,8,3]
83// Single_User - code for when user is simple single type
84// Vector_User - code for when user_t is a std::vector
85//
86// Then to run tests we use multiple inheritance of above classes.
87// Single_User_Single_GID - inherits Single_GID + Single_User
88// Single_User_Multiple_GID - inherits Multiple_GID + Single_User
89// Vector_User_Single_GID - inherits Single_GID + Vector_User
90// Vector_User_Multiple_GID - inherits Multiple_GID + Vector_User
91template <typename gid_t,typename lid_t,typename user_t>
92class IDs {
93 public:
94 // gcc requires this (not clang) - however it will not be used
95 // I think clang is handling this properly based on below public virtual
96 // inheritance but maybe will change this if the different compilers
97 // will handle it in non-uniform ways
98 IDs() {}
99
111 IDs(size_t totalIds_, size_t idBase_, size_t idStride_,
112 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
113 const std::string& test_name_, bool print_detailed_output_,
114 bool performance_test_, bool bUseLocalIDs_) :
115 totalIds(totalIds_), idBase(idBase_),
116 idStride(idStride_), comm(comm_), maxPrintSize(10), mode(mode_),
117 did_setup(false), test_name(test_name_),
118 print_detailed_output(print_detailed_output_),
119 performance_test(performance_test_), bUseLocalIDs(bUseLocalIDs_) {
120 }
121
122 // prints all the subsets used as utlity to create interesting overlaps
123 // prints all the update/remove/find decisions
125 // map out the behaviors of the subsets
126 comm->barrier();
127 if(comm->getRank() == 0) {
128 // some arbitrary sub sets used for testing coverage
129 std::cout << "Sub1: " << std::endl;
130 for(int proc = 0; proc < comm->getSize(); ++proc) {
131 std::cout << " rank " << proc << ": ";
132 for(size_t n = 0; n < totalIds; ++n) {
133 std::cout << subset1(n, proc);
134 }
135 std::cout << std::endl;
136 }
137
138 // some arbitrary sub sets using for testing coverage
139 std::cout << "Sub2: " << std::endl;
140 for(int proc = 0; proc < comm->getSize(); ++proc) {
141 std::cout << " rank " << proc << ": ";
142 for(size_t n = 0; n < totalIds; ++n) {
143 std::cout << subset2(n, proc);
144 }
145 std::cout << std::endl;
146 }
147
148 // update
149 std::cout << "update: " << std::endl;
150 for(int proc = 0; proc < comm->getSize(); ++proc) {
151 std::cout << " rank " << proc << ": ";
152 for(size_t n = 0; n < totalIds; ++n) {
153 std::cout << proc_update_gid(convert_index_to_gid(n), proc);
154 }
155 std::cout << std::endl;
156 }
157
158 // remove
159 std::cout << "remove: " << std::endl;
160 for(int proc = 0; proc < comm->getSize(); ++proc) {
161 std::cout << " rank " << proc << ": ";
162 for(size_t n = 0; n < totalIds; ++n) {
163 std::cout << proc_remove_gid(convert_index_to_gid(n), proc);
164 }
165 std::cout << std::endl;
166 }
167
168 // find
169 std::cout << "find: " << std::endl;
170 for(int proc = 0; proc < comm->getSize(); ++proc) {
171 std::cout << " rank " << proc << ": ";
172 for(size_t n = 0; n < totalIds; ++n) {
173 std::cout << proc_find_gid(convert_index_to_gid(n), proc);
174 }
175 std::cout << std::endl;
176 }
177 }
178 comm->barrier();
179 }
180
181 void printResultMessage(bool pass) const {
182 std::string base_message = get_test_name() + " mode: " + get_mode_name();
183 base_message = get_test_style() + " " + base_message;
184 std::string message = pass ?
185 " Passed: " + base_message :
186 " FAILED: " + base_message + " on rank " +
187 std::to_string(comm->getRank());
188 print_proc_safe(message, comm, pass); // only print passes on rank 0
189 }
190
191 const std::string & get_test_name() const { return test_name; }
192
195 virtual bool evaluateTests() const = 0;
196
199 int getMode() const { return mode; }
200
203 bool did_test_pass() const { return passed; }
204
210 virtual std::string get_test_style() const = 0;
211
214 void print() const
215 {
216 for(int proc = 0; proc < comm->getSize(); ++proc) {
217 comm->barrier();
218 if(proc == comm->getRank()) {
219 if(proc == 0) {
220 std::cout << std::endl <<
221 "############ Output: " << get_test_name()
222 << " ############" << std::endl;
223 }
224 std::cout << "Rank: " << proc << std::endl;
225 print_gids(update_gids, "Update gids" );
226 if(bUseLocalIDs) {
227 print_lids(update_lids, "Update lids" );
228 }
229 print_gids(remove_gids, "Remove gids" );
230 print_gids(find_gids, "Find gids" );
231 if(bUseLocalIDs) {
232 print_lids(find_lids, "Find lids" );
233 }
234 print_user_data(); // specific to single user or vector user type
235 if(bUseLocalIDs) {
236 print_lid_data(); // the lids we found, if bUseLocalIDs = true
237 }
238
239 if(proc == comm->getSize()-1) {
240 std::cout << std::endl;
241 }
242 }
243 }
244 comm->barrier();
245 }
246
247 protected:
248 std::string get_mode_name() const {
249 switch(mode) {
250 case TestMode::Add: return "Add"; break;
251 case TestMode::Replace: return "Replace"; break;
252 case TestMode::Aggregate: return "Aggregate"; break;
253 default: throw std::logic_error("Bad mode."); break;
254 }
255 }
256
257 // this is guaranteed to be true for at least one of the ranks
258 bool trueForAtLeastOneProc(int index, int rank) const {
259 return ((index % comm->getSize()) == rank);
260 }
261
262 // some arbitrary subset
263 bool subset1(int index, int rank) const {
264 return ((index % (comm->getSize()+1)) == rank);
265 }
266
267 // some arbitrary subset
268 bool subset2(int index, int rank) const {
269 return ((index % (comm->getSize()+3)) == rank);
270 }
271
272 // given a gid convert it to an index 0, 1, 2, ...
273 // this removes stride and then we use index to determine placement
274 // in some arbitrary subsets to make a mix of update, remove, and find gids
275 // it's abstract because multiple gid structs will be generated differently
276 virtual int convert_gid_to_index(const gid_t& gid) const = 0;
277
278 // reverse operation will bring an index back to a gid
279 // it's abstract because multiple gid structs will be generated differently
280 virtual gid_t convert_index_to_gid(int index) const = 0;
281
282 // decide if this rank will update this gid (and how many duplicates to send)
283 int proc_update_gid(gid_t gid, int rank) const {
284 // this method should be guaranteed to return true for 1 of the procs
285 // it can optionally return true for more than one to make a nice mix
286 // the return is the number of repeats as we want the directory to work
287 // in such a case
288 int index = convert_gid_to_index(gid); // back to 0,1,2,... indexing
289
290 // note not allowing duplicate gid for update - that will throw an error
291 // however we do allow duplicate find gid where each shoudl get the same
292 // value.
293 if(trueForAtLeastOneProc(index, rank) || subset1(index, rank)) {
294 return 1;
295 }
296 else {
297 return 0; // do not update this ID
298 }
299 }
300
301 // decide if this rank will find this gid (and how many duplicates to send)
302 int proc_find_gid(gid_t gid, int rank) const {
303 // this method can be anything - things should work even if this is empty;
304 int index = convert_gid_to_index(gid); // convert back to 0,1,2,...indexing
305
306 // for a few gid's duplicate - I keep this a small percentage because
307 // I want performance testing to be based on mostly unique ids.
308 int test_duplicate_count = 2;
309 if(subset1(index, rank)) {
310 return (index < 100) ? test_duplicate_count : 1;
311 }
312 else {
313 return 0; // do not read this ID
314 }
315 }
316
317 // decide if this rank will remove this gid (and how many duplicates to send)
318 int proc_remove_gid(gid_t gid, int rank) const {
319 // this should include some proc_find_gid values but not all, but also other
320 // values for good testing - we want to remove more than we wrote locally
321 // and check for proper removal
322 int index = convert_gid_to_index(gid); // convert back to 0,1,2,...indexing
323 int test_duplicate_count = 2;
324 if(subset2(index, rank)) {
325 return (index < 100) ? test_duplicate_count : 1;
326 }
327 else {
328 return 0; // do not remove this ID
329 }
330 }
331
332 // a special case for testing - we set the user data to a 'not found' state
333 // so we can confirm it was untouched if we call remove and then find on
334 // a gid which no longer exists.
335 virtual user_t get_not_found_user() const = 0;
336
337 // same as for above gid - we want to have the lid set to a known state
338 // ahead of time.
339 virtual lid_t get_not_found_lid() const = 0;
340
341 // fill the find_user with all the initial states
342 // this is only relevant for testing - not something a normal user will do
344 find_user = std::vector<user_t>(find_gids.size(), get_not_found_user());
345 }
346
347 // fill the find_lids with all the initial states
348 // this is only relevant for testing - not something a normal user will do
350 find_lids = std::vector<lid_t>(find_gids.size(), get_not_found_lid());
351 }
352
353 // a global check for the testing framework - did any proc remove this gid?
354 bool removedIDGlobally(gid_t gid) const {
355 for(int proc = 0; proc < comm->getSize(); ++proc) {
356 if(proc_remove_gid(gid, proc)) {
357 return true;
358 }
359 }
360 return false;
361 }
362
363 // a check for the testing framework - count how many times this gid was
364 // updated (currently we allow duplicate gids in one update call). This will
365 // count each case as +1.
366 int sharedCount(gid_t gid) const {
367 int count = 0;
368 for(int proc = 0; proc < comm->getSize(); ++proc) {
369 count += proc_update_gid(gid, proc);
370 }
371 return count;
372 }
373
374 // utility function to output a list of gids
375 // handles the gid as a struct as well using gid_to_string
376 // Note the testing framework (here) 'knows' how to deciper the structure
377 // of the gid but the directory does not, hence the directory doesn't have
378 // the ability to print out what the struct contents are. We might want to
379 // have some kind of print() method required for these structs to make log
380 // outputs easier. Something like gid_to_string implemented in this file.
381 void print_gids(const std::vector<gid_t> &printIds, std::string name) const {
382 std::cout << " " << printIds.size() << " " << name << ": ";
383 for (size_t i = 0; i < printIds.size() && i < maxPrintSize; i++) {
384 std::cout << gid_to_string(printIds[i]) << " ";
385 }
386 if(printIds.size() > maxPrintSize) {
387 std::cout << "... ";
388 }
389 std::cout << std::endl;
390 }
391
392 // utility function to output a list of gids
393 // handles the lid as a struct as well using lid_to_string
394 void print_lids(const std::vector<lid_t> &printIds, std::string name) const {
395 std::cout << " " << printIds.size() << " " << name << ": ";
396 for (size_t i = 0; i < printIds.size() && i < maxPrintSize; i++) {
397 std::cout << lid_to_string(printIds[i]) << " ";
398 }
399 if(printIds.size() > maxPrintSize) {
400 std::cout << "... ";
401 }
402 std::cout << std::endl;
403 }
404
405 // user data can be simple of std::vector so handled by derived classes
406 virtual void print_user_data() const = 0;
407
408 // prints out the list of lids we found
409 virtual void print_lid_data() const {
410 std::cout << "Find LIDs ";
411 for(size_t n = 0; n < this->find_gids.size(); ++n) {
412 std::cout << this->gid_to_string(this->find_gids[n]) << ":" <<
413 this->lid_to_string(this->find_lids[n]) << " ";
414 }
415 std::cout << std::endl;
416 }
417
418 // to make some fixed conversions between gid and arbitrary user data the
419 // seed value is used. This is so the testing can validate final results are
420 // correct based on gid. For gid as a struct, this just uses the first
421 // element in the gid array.
422 virtual size_t gid_seed_value(const gid_t& gid) const = 0;
423
424 // convert gid to nice output - handled gid as a struct if that is the case
425 virtual std::string gid_to_string(gid_t gid) const = 0;
426
427 // convert lid to nice output - handled gid as a struct if that is the case
428 virtual std::string lid_to_string(lid_t lid) const = 0;
429
430 // determine if the two lids are equal - used by testing to validate result
431 // matches expected result.
432 virtual bool check_lid_equal(const lid_t & a, const lid_t & b) const = 0;
433
434 // get_expected_user is the user data we expect to have returned after a
435 // find command. It would be the same as get_initial_value for Replace mode
436 // but for Add and Aggregate, this is the calculated result we expect the
437 // directory to have performed. So for example two difference procs may both
438 // update in Aggregate mode with a different vector. They would each have
439 // their own get_initial_user, say [1,3,5] for rank 0 and [1,5,7] for rank 1
440 // but that is for the same gid. That is why get_initial_user takes both
441 // gid and rank. However get_expected_user is the grand result which would
442 // be [1,3,5,7] and independent of rank. The tests here check the results
443 // returned by the directory find command and make sure they match the
444 // value calculated by get_expected_user.
445 virtual user_t get_expected_user(gid_t gid) const = 0;
446
447 // this is the user data the specific rank sent for gid
448 // for Add and Aggregate modes different ranks will be sending different
449 // data for the same gid. The final result that the directory is supposed
450 // to generate is calculated as get_expected_user above for verification.
451 virtual user_t get_initial_user(gid_t gid, int rank) const = 0;
452
453 // for a given gid, this determines some arbitrary lid to go with it. This
454 // value is sent to the directory and then can be checked later to be sure
455 // we got the right lid back.
456 virtual lid_t get_initial_lid(gid_t gid) const = 0;
457
458 // execute() is called by the derived class so that inheritance will be
459 // executing for all methods properly. This method handles everything.
460 virtual void execute() {
461 setup(); // get things ready
462 test(); // run the actual directory code
463 analyze(); // analyze the results
464 output(); // print results
465 // debug_print_subsets_and_decisions();
466 }
467
468 // preparation for running the test
469 virtual void setup() {
470 // sanity check - make sure we have our internal logic correct - in case
471 // the class hierarchy gets more complicated this is a reminder to make
472 // sure the execute() method is being called once by the highest level.
473 if(did_setup) {
474 throw std::logic_error(
475 "setup already called - execute once in highest level class only.");
476 }
477 did_setup = true;
478
479 // sanity check on the proc_update_gid
480 // this verifies that at least one proc is actually going to write each
481 // gid which is not really a requirement but more to make sure the tests
482 // are running what we expected.
483 for(size_t n = 0; n < totalIds; ++n) {
484 gid_t gid = convert_index_to_gid(n);
485 bool bAtLeastOne = false;
486 for(int proc = 0; proc < comm->getSize(); ++proc) {
487 if(proc_update_gid(gid, proc)) {
488 bAtLeastOne = true;
489 }
490 }
491 if(!bAtLeastOne) {
492 throw std::logic_error("The proc_update_gid method must generate an"
493 " algorithm which returns true for at least 1 proce for all IDs. If"
494 " this is changed then the findID algorithm should not return true"
495 " for any of those missing IDs. This is a sanity check.");
496 }
497 }
498
499 // now decide which gids we will update, remove, and find
500 // this is somewhat arbitrary and the methods here are use to make some
501 // nice mix up.
502 for(size_t n = 0; n < totalIds; ++n) {
503 // first generate the gid from index (handles stride, base offset)
504 gid_t gid = convert_index_to_gid(n);
505
506 // determine how many of this particular gid we will update
507 int count_proc_update_gid = proc_update_gid(gid, comm->getRank());
508 for(int i = 0; i < count_proc_update_gid; ++i) {
509 update_gids.push_back(gid);
510 update_lids.push_back(get_initial_lid(gid));
511 }
512
513 // now make the lists of gids we will remove
514 // there is no purpose here - we just want to update a bunch, then
515 // remove some, then find some. The testing here will purposefully try
516 // to find gids which were removed and then verify they are in fact
517 // no longer in the directory.
518 for(int i = 0; i < proc_remove_gid(gid, comm->getRank()); ++i) {
519 remove_gids.push_back(gid);
520 }
521
522 // now make the list of gids we will find
523 for(int i = 0; i < proc_find_gid(gid, comm->getRank()); ++i) {
524 find_gids.push_back(gid);
525 }
526 }
527
528 // set up all the initial writes - update_user will be send to the
529 // directory with these values for Replace, Add, or Aggregate operations.
530 this->update_user.resize(update_gids.size());
531 for(size_t n = 0; n < update_user.size(); ++n) {
532 update_user[n] = get_initial_user(update_gids[n], comm->getRank());
533 }
534 }
535
536 // this is where we actually make a directory and do things on it
537 virtual void test() = 0;
538
539 template<typename directory_t>
541 const int debug_level = 0;
542 directory_t zz1(comm, bUseLocalIDs, debug_level);
543
544 // this step is not necessary but implemented to provide coverage of the
545 // copy constructor and the operator= method.
546 // Can be removed and then change above zz1 to just zz
547 directory_t zz2(zz1);
548 directory_t zz = zz2;
549
550 // usually this step is not necessary - for this test we initialize all
551 // the find_user values to a specific number and then check that it's not
552 // changed when we call find on an ID which was previously removed. The
553 // formal behavior for this in a normal setup is probably to just have an
554 // error but in this special case we override the throw when we call find.
556
557 // for lid note that the original mode will leave a not found gid such
558 // that the lid is set to the gid. This is how the find_local worked. In
559 // the new directory the value will be left untouched so this will be
560 // preserved. Also original mode requires this to be set but for new
561 // modes with std::vector we can make this fillled automatically.
562 if(bUseLocalIDs) { // if false it shouldn't matter if we call this
564 }
565
566 // convert generic mode to Zoltan2Directory mode. This awkward step exists
567 // because the Original mode doesn't have a directory since it's the
568 // original zoltan code. When Original mode is running the new directory
569 // code does not exist and the directory_t::Update_Mode doesn't exist.
570 // So we have generic mode (just for the unit test) which everything can
571 // understand and then convert to the directory mode here since this is
572 // not Original mode. This could probaly be done better but it helped
573 // to avoid some special casing through out.
574 auto directoryMode = directory_t::Update_Mode::Add;
575 switch(mode) {
576 case Add:
577 directoryMode = directory_t::Update_Mode::Add;
578 break;
579 case Replace:
580 directoryMode = directory_t::Update_Mode::Replace;
581 break;
582 case Aggregate:
583 directoryMode = directory_t::Update_Mode::Aggregate;
584 break;
585 }
586
587 // now create the directory data with update - this will proces Replace,
588 // Add, or Aggregate, depending on the directoryMode.
589 zz.update(update_gids.size(), &update_gids[0],
590 bUseLocalIDs ? &update_lids[0] : NULL,
591 &update_user[0], NULL, directoryMode);
592
593 zz.remove(remove_gids.size(), &remove_gids[0]);
594
595 // Now call find which will fill find_user with the correct data.
596 // Some of the tests use lids and will also fill find_lids with lid values.
597 zz.find(find_gids.size(), &find_gids[0],
598 bUseLocalIDs ? &find_lids[0] : NULL,
599 &find_user[0], NULL, NULL, false);
600 }
601
602 // set passed true/false based on the results gotten back from the directory
603 // here we use the methods of this class to calculate what the directory is
604 // supposed to have done and then compare to see if it's correct.
605 void analyze() {
607 }
608
609 // prints a bunch of output depending on flag settings
610 // this was mainly used for debugging but might be useful for learning about
611 // the tests. This is setup as a hard coded value named print_output which
612 // is set in runDirectoryTests.
613 void output() {
615 print();
616 }
617 if(!performance_test) {
619 }
620 }
621
622 // data stored by the class
623 size_t totalIds; // total gids across all procs
624 size_t idBase; // first gid value
625 size_t idStride; // stride between gid values
626 Teuchos::RCP<const
627 Teuchos::Comm<int> > comm; // communicator sent to directory
628 size_t maxPrintSize; // print only this number for debugging
629 int mode; // Replace, Add, or Aggregate
630 std::vector<gid_t> update_gids; // gids generated on this proc
631 std::vector<lid_t> update_lids; // corresponding lids generated on this proc
632 std::vector<user_t> update_user; // user data we initially updated with
633 std::vector<gid_t> find_gids; // gids to read
634 std::vector<lid_t> find_lids; // lids will be filled
635 std::vector<user_t> find_user; // user data we find
636 std::vector<gid_t> remove_gids; // gids to remove
637 bool did_setup; // error check setup not called twice
638 std::string test_name; // used for logging
639 bool passed; // did we pass
640 bool print_detailed_output; // log all the details
641 bool performance_test; // is this being run as a performance test
642 bool bUseLocalIDs; // should local ids be tested/applied
643};
644
645// Test classes are built using one of these:
646// Single_GID - gid is not a struct - just simple type like long
647// Multiple_GID - gid is a struct such as struct { int a[4]; }
648// The two classes exist to handle all the special casing when using the
649// two different types of gid.
650// see base class abstract definitions for comments on each method
651template <typename gid_t,typename lid_t, typename user_t>
652class Single_GID : public virtual IDs<gid_t,lid_t,user_t>
653{
654 public:
656
657 protected:
658 virtual lid_t get_not_found_lid() const {
659 return NOT_FOUND_VALUE;
660 }
661
662 virtual std::string gid_to_string(gid_t gid) const {
663 return "(" + std::to_string(gid) + ")";
664 };
665
666 virtual std::string lid_to_string(lid_t lid) const {
667 return "(" + std::to_string(lid) + ")";
668 };
669
670 virtual bool check_lid_equal(const lid_t & a, const lid_t & b) const {
671 return (a == b);
672 }
673
674 virtual size_t gid_seed_value(const gid_t& gid) const {
675 return gid;
676 }
677
678 virtual int convert_gid_to_index(const gid_t& gid) const {
679 return (gid-this->idBase)/this->idStride;
680 }
681
682 virtual gid_t convert_index_to_gid(int index) const {
683 return this->idBase + index * this->idStride;
684 }
685
686 virtual lid_t get_initial_lid(gid_t gid) const {
687 return gid + 1; // this is for testing - make the lid equal to gid+1
688 }
689};
690
691// Test classes are built using one of these:
692// Single_GID - gid is not a struct - just simple type like long
693// Multiple_GID - gid is a struct such as struct { int a[4]; }
694// The two classes exist to handle all the special casing when using the
695// two different types of gid.
696// see base class abstract definitions for comments on each method
697template <typename gid_t,typename lid_t, typename user_t>
698class Multiple_GID : public virtual IDs<gid_t,lid_t,user_t>
699{
700 public:
701 Multiple_GID(size_t gid_length_, size_t lid_length_) :
702 gid_length(gid_length_), lid_length(lid_length_) {
703 // currently we're making the testing for multiple gid cover
704 // multiple lid as well - further combinations would be a gid multiple
705 // type such as [1,4,5,8] and a simple lid type ( such as int ).
706 }
707
708 protected:
709 virtual lid_t get_not_found_lid() const {
710 lid_t not_found;
711 for(size_t n = 0; n < lid_length; ++n) {
712 not_found.sub_lid[n] = NOT_FOUND_VALUE;
713 }
714 return not_found;
715 }
716
717 virtual std::string gid_to_string(gid_t gid) const {
718 std::string output_string = "(";
719 for(size_t n = 0; n < gid_length; ++n) {
720 if(n!=0) output_string += " ";
721 output_string += std::to_string(gid.sub_gid[n]);
722 }
723 output_string += ")";
724 return output_string;
725 };
726
727 virtual std::string lid_to_string(lid_t lid) const {
728 std::string output_string = "(";
729 for(size_t n = 0; n < lid_length; ++n) {
730 if(n!=0) output_string += " ";
731 output_string += std::to_string(lid.sub_lid[n]);
732 }
733 output_string += ")";
734 return output_string;
735 };
736
737 virtual bool check_lid_equal(const lid_t & a, const lid_t & b) const {
738 for(size_t n = 0; n < lid_length; ++n) {
739 if(a.sub_lid[n] != b.sub_lid[n]) {
740 return false;
741 }
742 }
743 return true;
744 }
745
746 virtual size_t gid_seed_value(const gid_t& gid) const {
747 return gid.sub_gid[0]; // just uses first to generate values
748 }
749
750 virtual int convert_gid_to_index(const gid_t& gid) const {
751 // here we are testing gid with multiple elements
752 // the first element is set using the same rules as for single gid
753 // so just read that and convert back to index
754 return (gid.sub_gid[0]-this->idBase)/this->idStride;
755 }
756
757 virtual gid_t convert_index_to_gid(int index) const {
758 gid_t gid;
759 // here we are testing gid with multiple elements
760 // set the first element as if it was a single gid - same as other tests
761 // set all subsequent gid sub ids in increasing order just to have change
762 // the values don't have any impact on the behavior of the directory
763 int val = this->idBase + index * this->idStride;
764 for(size_t n = 0; n < gid_length; ++n) {
765 gid.sub_gid[n] = val + n;
766 }
767 return gid;
768 }
769
770 virtual lid_t get_initial_lid(gid_t gid) const {
771 lid_t result;
772 for(size_t n = 0; n < lid_length; ++n) {
773 result.sub_lid[n] = n + gid.sub_gid[0] + 1; // lid will be gid[0]+1, gid[1]+2, gid[2]+3...
774 }
775 return result;
776 }
777
778 private:
779 size_t gid_length;
780 size_t lid_length;
781};
782
783// Test classes are built using one of these:
784// Single_User - user data is a simple type like long
785// Vector_User - user data is a std::vector<>
786// The two classes exist to handle all the special casing when using the
787// two different types of user data.
788// see base class abstract definitions for comments on each method
789template <typename gid_t,typename lid_t, typename user_t>
790class Single_User : public virtual IDs<gid_t,lid_t,user_t>
791{
792 public:
794
795 protected:
796 virtual void test() {
798 directory_t;
799 this->template test_implement<directory_t>();
800 }
801
802 virtual user_t get_not_found_user() const {
803 return NOT_FOUND_VALUE;
804 }
805
806 virtual user_t get_expected_user(gid_t gid) const {
807 switch(this->mode) {
809 return this->get_initial_user(gid, this->comm->getRank());
810 break;
811 case TestMode::Add:
812 return this->sharedCount(gid); // should have summed
813 break;
815 throw std::logic_error("Unexpected aggregate mode for non array.");
816 break;
817 default:
818 throw std::logic_error("Unexpected mode index.");
819 break;
820 }
821 }
822
823 virtual user_t get_initial_user(gid_t gid, int rank) const {
824 switch(this->mode) {
826 // arbitrary - just make it something good to test
827 // note that replace is a tricky case because two procs could both
828 // try to update the same gid with different values. Then the result
829 // could be arbitrary based on how the messages are passed.
830 // For the testing code here the value is fixed to be the same for
831 // all procs but the question to resolve is should the directory
832 // handle conflicting replace calls with an error or simply assume the
833 // user is responsible for making it logically consistent.
834 return this->gid_seed_value(gid) + 3;
835 break;
836 case TestMode::Add:
837 return 1; // we will be testing if they sum to shared count
838 break;
840 throw std::logic_error("Aggregate requested for non array setup.");
841 break;
842 default:
843 throw std::logic_error("Unexpected mode index.");
844 break;
845 }
846 }
847
848 virtual bool evaluateTests() const {
849
850 // check the results
851 bool pass = true;
852 for(int proc = 0; proc < this->comm->getSize(); ++proc) {
853 bool passRank = true;
854 this->comm->barrier();
855 if(proc == this->comm->getRank()) {
856 for(size_t i = 0; i < this->find_gids.size(); ++i) {
857 gid_t gid = this->find_gids[i];
858
859 // verify removal - eventually I think we may have the directory
860 // just throw an error if we try to find on a gid which was removed.
861 // Or some return value will indicate. For now we pass in all values
862 // of NOT_FOUND_VALUE which will still be set to that if not found.
863 if(this->removedIDGlobally(gid)) {
864 if(this->find_user[i] != NOT_FOUND_VALUE) {
865 passRank = false;
866 std::cout << "Removed gid: " << this->gid_to_string(gid) <<
867 " but got value: " << this->find_user[i] <<
868 " when we expected to read the unset value of " <<
869 NOT_FOUND_VALUE << ". " << " This is incorrect. " <<
870 "Remove FAILED." << std::endl;
871 }
872 }
873 else {
874 user_t expected_value = this->get_expected_user(gid);
875 if(this->find_user[i] != expected_value) {
876 passRank = false;
877 std::cout << "Failed read user data for global ID: " <<
878 this->gid_to_string(gid) << ". Expected data: " <<
879 expected_value << " Got data: " <<
880 this->find_user[i] << std::endl;
881 }
882
883 if(this->bUseLocalIDs) {
884 const lid_t & find_lid = this->find_lids[i];
885 lid_t expected_lid = this->get_initial_lid(gid);
886 if(!this->check_lid_equal(find_lid, expected_lid)) {
887 passRank = false;
888 std::cout << "Failed read lid for global ID: " <<
889 this->gid_to_string(gid) << ". Expected lid: " <<
890 this->lid_to_string(expected_lid) << " Got lid: " <<
891 this->lid_to_string(find_lid) << std::endl;
892 }
893 }
894 }
895
896 if(!passRank) {
897 break; // we don't need to check further
898 }
899 }
900
901 if(!passRank) {
902 std::cout << "Checked rank: " << this->comm->getRank() <<
903 " with nIds: " << this->find_gids.size() << " which " <<
904 "FAILED" << std::endl;
905 }
906
907 if(!passRank) {
908 pass = false;
909 }
910 }
911 }
912 this->comm->barrier();
913
914 return pass;
915 }
916
917 virtual void print_user_data() const {
918 std::cout << "Write GID user data ";
919 for(size_t n = 0; n < this->update_gids.size(); ++n) {
920 std::cout << this->gid_to_string(this->update_gids[n]) << ":" <<
921 this->update_user[n] << " ";
922 }
923 std::cout << std::endl;
924
925 std::cout << "Find GID user data ";
926 for(size_t n = 0; n < this->find_gids.size(); ++n) {
927 std::cout << this->gid_to_string(this->find_gids[n]) << ":" <<
928 this->find_user[n] << " ";
929 }
930 std::cout << std::endl;
931 }
932};
933
934// Test classes are built using one of these:
935// Single_User - user data is a simple type like long
936// Vector_User - user data is a std::vector<>
937// The two classes exist to handle all the special casing when using the
938// two different types of user data.
939// see base class abstract definitions for comments on each method
940template <typename gid_t,typename lid_t, typename user_t>
942{
943 public:
945
946 protected:
947 virtual void test() {
949 directory_t;
950 this->template test_implement<directory_t>();
951 }
952
953 virtual user_t get_not_found_user() const {
954 return user_t(1, NOT_FOUND_VALUE);
955 }
956
957 virtual user_t get_expected_user(gid_t gid) const {
958 switch(this->mode) {
959 case TestMode::Add:
960 return user_t(test_create_array_length(gid, this->comm->getRank()),
961 this->sharedCount(gid)); // should have summed
962 break;
964 return get_initial_user(gid, this->comm->getRank());
965 break;
967 {
968 // for this we need the union of all the procs
969 user_t final_aggregated;
970 // loop through all possible updaters
971 for(int proc = 0; proc < this->comm->getSize(); ++proc) {
972 if(this->proc_update_gid(gid, proc)) { // did this proc update?
973 user_t proc_input = get_initial_user(gid, proc); // get original
974 for(size_t i = 0; i < proc_input.size(); ++i) { // scan elements
975 auto val = proc_input[i]; // get the array element
976 if(final_aggregated.size() == 0 ||
977 val > final_aggregated[final_aggregated.size()-1]) {
978 // add first element or tail end
979 final_aggregated.push_back(val);
980 }
981 else { // loop and insert to keep ordering until match found
982 for(auto itr = final_aggregated.begin();
983 itr != final_aggregated.end(); ++itr) {
984 if((*itr) == val) {
985 break; // don't add - already added
986 }
987 else if((*itr) > val) {
988 final_aggregated.insert(itr, val);
989 break; // insert here to keep ordering
990 }
991 }
992 }
993 }
994 }
995 }
996 return final_aggregated;
997 }
998 break;
999 default:
1000 throw std::logic_error("Unexpected mode index.");
1001 break;
1002 }
1003 }
1004
1005 virtual user_t get_initial_user(gid_t gid, int rank) const {
1006 // determine length of array
1007 size_t modLength = test_create_array_length(gid, rank);
1008 user_t array(modLength);
1009 for(size_t n = 0; n < array.size(); ++n) {
1010 switch(this->mode) {
1011 case TestMode::Replace:
1012 array[n] = this->gid_seed_value(gid) + 3; // 3 is arbitrary
1013 break;
1014 case TestMode::Add:
1015 array[n] = 1; // all elements sum to comm->getSize()
1016 break;
1018 // Now we want some mix so that, for example, gid 10 will have
1019 // different but overlapping values for each array element n
1020 // For example proc 1 could update with gid=10 array={5,7,9}
1021 // while proc 2 could update with gid=10 array={3,5,7}
1022 // Then we expect the result to be {3,5,7,9}
1023 array[n] = n + rank*2; // this creates overlapping regions
1024 break;
1025 default:
1026 throw std::logic_error("Unexpected mode index.");
1027 break;
1028 }
1029 }
1030 return array;
1031 }
1032
1033 virtual bool evaluateTests() const {
1034
1035 // check the results
1036 bool pass = true;
1037 for(int proc = 0; proc < this->comm->getSize(); ++proc) {
1038 bool passRank = true;
1039 this->comm->barrier();
1040 if(proc == this->comm->getRank()) {
1041 for(size_t i = 0; i < this->find_gids.size(); ++i) {
1042 gid_t gid = this->find_gids[i];
1043
1044 // verify removal - eventually I think we may have the directory
1045 // just throw an error if we try to find on a gid which was removed.
1046 // Or some return value will indicate. For now we pass in all values
1047 // of NOT_FOUND_VALUE which will still be set to that if not found.
1048 if(this->removedIDGlobally(gid)) {
1049 // should be an array of size 1 with element NOT_FOUND_VALUE
1050 if(this->find_user[i].size() != 1 ||
1051 this->find_user[i][0] != NOT_FOUND_VALUE) {
1052 passRank = false;
1053 std::cout << "Removed array for gid: " <<
1054 this->gid_to_string(gid) <<
1055 " but something set the user data which is incorrect. "
1056 "Remove FAILED." << std::endl;
1057 }
1058 }
1059 else {
1060 user_t expected_value = get_expected_user(gid);
1061 // first validate the array length is correct
1062 if(this->find_user[i].size() != expected_value.size()) {
1063 std::cout << " Rank: " << proc << " array size is incorrect for"
1064 " gid: " << this->gid_to_string(gid) << ". Expected size: " <<
1065 expected_value.size() << " and got size: " <<
1066 this->find_user[i].size() << std::endl;
1067 passRank = false;
1068 break;
1069 }
1070
1071 // TODO: Fix this code duplicated in the other evaluateTests
1072 // Generall these two methdos can perhaps be merged better now
1073 if(this->bUseLocalIDs) {
1074 const lid_t & find_lid = this->find_lids[i];
1075 lid_t expected_lid = this->get_initial_lid(gid);
1076 if(!this->check_lid_equal(find_lid, expected_lid)) {
1077 passRank = false;
1078 std::cout << "Failed read lid for global ID: " <<
1079 this->gid_to_string(gid) << ". Expected lid: " <<
1080 this->lid_to_string(expected_lid) << " Got lid: " <<
1081 this->lid_to_string(find_lid) << std::endl;
1082 }
1083 }
1084
1085 // now loop the elements and validate each individual element
1086 for(size_t arrayIndex = 0; arrayIndex < this->find_user[i].size()
1087 && passRank; ++arrayIndex) {
1088 if(this->find_user[i][arrayIndex] != expected_value[arrayIndex]) {
1089 passRank = false;
1090 std::cout << " Failed vector read for global ID: " <<
1091 this->gid_to_string(gid)
1092 << ". Expected: " << expected_value[arrayIndex] <<
1093 " at array index " << arrayIndex << ". Got: " <<
1094 this->find_user[i][arrayIndex] << std::endl;
1095 }
1096 }
1097 }
1098
1099 if(!passRank) {
1100 break; // we don't need to check further
1101 }
1102 }
1103
1104 if(!passRank) {
1105 std::cout << " Checked rank: " << this->comm->getRank() <<
1106 " with num find gids: " << this->find_gids.size() << " which " <<
1107 "FAILED" << std::endl;
1108 }
1109
1110 if(!passRank) {
1111 pass = false;
1112 }
1113 }
1114 }
1115 this->comm->barrier();
1116
1117 return pass;
1118 }
1119
1120 virtual void print_user_data() const {
1121 for(size_t n = 0; n < this->update_gids.size(); ++n) {
1122 std::cout << " Write array for GID " <<
1123 this->gid_to_string(this->update_gids[n]) << ": ";
1124 for(size_t a = 0; a < this->update_user[n].size(); ++a) {
1125 std::cout << this->update_user[n][a] << " ";
1126 }
1127 std::cout << std::endl;
1128 }
1129 for(size_t n = 0; n < this->find_gids.size(); ++n) {
1130 std::cout << " Read array for GID " <<
1131 this->gid_to_string(this->find_gids[n]) << ": ";
1132 for(size_t a = 0; a < this->find_user[n].size(); ++a) {
1133 std::cout << this->find_user[n][a] << " ";
1134 }
1135 std::cout << std::endl;
1136 }
1137 }
1138
1139 virtual size_t test_create_array_length(gid_t gid, int proc) const {
1140 switch(this->mode) {
1141 case Replace:
1142 // replace is in some ways the trickiest because Add and Aggregate
1143 // are not order dependent. If two different procs both try to update
1144 // the same gid with replace the final result may be arbitrary ordering
1145 // and not well defined. Should the directory be responsible for
1146 // detecting a logic error from the user? Note this issue is not a
1147 // vector issue, but a general issue with replace. For now we assume
1148 // the user is consistent and never sends conflicting calls. Replace
1149 // uses the same value for type or vector and is independent of rank.
1150 return (this->gid_seed_value(gid)%7); // 1..7 inclusive same as Add
1151 break;
1152 case Add:
1153 // in this case all vector lengths must be identical for each proc
1154 // or the directory gives an error - cannot add length 2 and length 8
1155 return (this->gid_seed_value(gid)%7); // 1..7 inclusive
1156 break;
1157 case Aggregate:
1158 // in this case we want different proc but same gid to make different
1159 // vectors - so for example mix a length 3 with a length 8
1160 return (this->gid_seed_value(gid)%7)+proc; // varies per gid and proc
1161 break;
1162 default:
1163 throw std::logic_error("test_create_array_length bad mode.");
1164 }
1165 }
1166};
1167
1168// These classes build the test using a combination of the following classes:
1169// Single_User or Vector_User
1170// Single_GID or Multiple_GID
1171// The class just calls execute and exists to assemble the required inheritance.
1172template <typename gid_t,typename lid_t, typename user_t>
1175{
1176 public:
1177 Single_User_Single_GID(size_t totalIds_, size_t idBase_, size_t idStride_,
1178 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
1179 const std::string& name_, bool print_detailed_output_,
1180 bool performance_test_, bool bUseLocalIDs_) :
1181 IDs<gid_t, lid_t, user_t>(totalIds_, idBase_, idStride_, comm_, mode_,
1182 name_, print_detailed_output_, performance_test_, bUseLocalIDs_),
1183 Single_User<gid_t, lid_t, user_t>(),
1184 Single_GID<gid_t, lid_t, user_t>() {
1185 this->execute();
1186 }
1187
1188 virtual std::string get_test_style() const {
1189 return "Single_User_Single_GID";
1190 }
1191};
1192
1193// These classes build the test using a combination of the following classes:
1194// Single_User or Vector_User
1195// Single_GID or Multiple_GID
1196// The class just calls execute and exists to assemble the required inheritance.
1197template <typename gid_t,typename lid_t, typename user_t>
1200{
1201 public:
1202 Single_User_Multiple_GID(size_t gid_length_, size_t lid_length_,
1203 size_t totalIds_, size_t idBase_, size_t idStride_,
1204 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
1205 const std::string& name_, bool print_detailed_output_,
1206 bool performance_test_, bool bUseLocalIDs_) :
1207 IDs<gid_t, lid_t, user_t>(totalIds_, idBase_, idStride_, comm_, mode_,
1208 name_, print_detailed_output_, performance_test_, bUseLocalIDs_),
1209 Single_User<gid_t, lid_t, user_t>(),
1210 Multiple_GID<gid_t, lid_t, user_t>(gid_length_, lid_length_) {
1211 this->execute();
1212 }
1213
1214 virtual std::string get_test_style() const {
1215 return "Single_User_Multiple_GID";
1216 }
1217};
1218
1219// These classes build the test using a combination of the following classes:
1220// Single_User or Vector_User
1221// Single_GID or Multiple_GID
1222// The class just calls execute and exists to assemble the required inheritance.
1223template <typename gid_t,typename lid_t, typename user_t>
1226{
1227 public:
1228 Vector_User_Single_GID(size_t totalIds_, size_t idBase_, size_t idStride_,
1229 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
1230 const std::string& name_, bool print_detailed_output_,
1231 bool performance_test_, bool bUseLocalIDs_) :
1232 IDs<gid_t, lid_t, user_t>(totalIds_, idBase_, idStride_, comm_, mode_,
1233 name_, print_detailed_output_, performance_test_, bUseLocalIDs_),
1234 Vector_User<gid_t, lid_t, user_t>(),
1235 Single_GID<gid_t, lid_t, user_t>() {
1236 this->execute();
1237 }
1238
1239 virtual std::string get_test_style() const {
1240 return "Vector_User_Single_GID";
1241 }
1242};
1243
1244// These classes build the test using a combination of the following classes:
1245// Single_User or Vector_User
1246// Single_GID or Multiple_GID
1247// The class just calls execute and exists to assemble the required inheritance.
1248template <typename gid_t,typename lid_t, typename user_t>
1251{
1252 public:
1253 Vector_User_Multiple_GID(size_t gid_length_, size_t lid_length_,
1254 size_t totalIds_, size_t idBase_, size_t idStride_,
1255 Teuchos::RCP<const Teuchos::Comm<int> > &comm_, int mode_,
1256 const std::string& name_, bool print_detailed_output_,
1257 bool performance_test_, bool bUseLocalIDs_) :
1258 IDs<gid_t, lid_t, user_t>(totalIds_, idBase_, idStride_, comm_, mode_,
1259 name_, print_detailed_output_, performance_test_, bUseLocalIDs_),
1260 Vector_User<gid_t, lid_t, user_t>(),
1261 Multiple_GID<gid_t, lid_t, user_t>(gid_length_, lid_length_) {
1262 this->execute();
1263 }
1264
1265 virtual std::string get_test_style() const {
1266 return "Vector_User_Multiple_GID";
1267 }
1268};
1269
1270// The TestManager holds some common values and provides 4 API calls for running
1271// tests using the different options. The Multiple GID option is just for
1272// setting up tests as the directory class itself works automatically to handle
1273// gid_t of int or a struct. For Vector User this is currently handled by
1274// a different named directory class but eventually may merge these back into
1275// a single class and just let the API distinguish. However I'm not sure about
1276// how to make that work cleanly with the templating. The 4 testing modes are:
1277// Single User + Single GID (ex. user_t int, gid=8)
1278// Single User + Multiple GID (ex. user_t int, gid=[0,1,8])
1279// Vector User + Single GID (ex. user_t std::vector<int>, gid=8)
1280// Vector User + Multiple GID (ex. user_t std::vector<int>, gid=[0,1,8]
1281// The point of this is to make sure this unit test actually tries all these
1282// combinations and fails if one is missing. Only Kokkos mode supports everything.
1284 public:
1285 TestManager(Teuchos::RCP<const Teuchos::Comm<int> > comm_, int totalIds_,
1286 bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_) :
1287 comm(comm_), totalIds(totalIds_), all_pass(true),
1288 print_detailed_output(print_detailed_output_),
1289 performance_test(performance_test_), bUseLocalIDs(bUseLocalIDs_) {
1290 }
1291
1292 // Single User + Single GID
1293 template<typename gid_t, typename lid_t, typename user_t>
1294 void run_single_user_single_gid(const std::string& name_,
1295 int mode_, size_t idBase_, int idStride_) {
1297 idBase_, idStride_, comm, mode_, name_, print_detailed_output,
1298 performance_test, bUseLocalIDs);
1299 if(!ids.did_test_pass()) { all_pass = false; }
1300 }
1301
1302 // Single User + Multiple GID
1303 template<typename gid_t, typename lid_t, typename user_t>
1304 void run_single_user_multiple_gid(size_t gid_length_, size_t lid_length_,
1305 const std::string& name_, int mode_, size_t idBase_, int idStride_) {
1306 Single_User_Multiple_GID<gid_t,lid_t,user_t> ids(gid_length_, lid_length_,
1307 totalIds, idBase_, idStride_, comm, mode_, name_, print_detailed_output,
1308 performance_test, bUseLocalIDs);
1309 if(!ids.did_test_pass()) { all_pass = false; }
1310 }
1311
1312 // Vector User + Single GID
1313 template<typename gid_t, typename lid_t, typename user_t>
1314 void run_vector_user_single_gid(const std::string& name_,
1315 int mode_, size_t idBase_, int idStride_) {
1317 idBase_, idStride_, comm, mode_, name_, print_detailed_output,
1318 performance_test, bUseLocalIDs);
1319 if(!ids.did_test_pass()) { all_pass = false; }
1320 }
1321
1322 // Vector User + Multiple GID
1323 template<typename gid_t, typename lid_t, typename user_t >
1324 void run_vector_user_multiple_gid(size_t gid_length_, size_t lid_length_,
1325 const std::string& name_, int mode_, size_t idBase_, int idStride_) {
1326 Vector_User_Multiple_GID<gid_t,lid_t,user_t> ids(gid_length_, lid_length_,
1327 totalIds, idBase_, idStride_, comm, mode_, name_, print_detailed_output,
1328 performance_test, bUseLocalIDs);
1329 if(!ids.did_test_pass()) { all_pass = false; }
1330 }
1331
1332 bool did_all_pass() const { return all_pass; }
1333
1334 private:
1335 Teuchos::RCP<const Teuchos::Comm<int> > comm;
1336 int totalIds;
1337 bool all_pass;
1338 bool print_detailed_output;
1339 bool performance_test;
1340 bool bUseLocalIDs;
1341};
1342
1343// This is a systematic grind through all the possible options the directory
1344// can do and makes use of above classes to hit on various features like
1345// vector user type versus single user type, multiple gid, different types,
1346// etc. This is intended to be a thorough test of the directory but is
1347// not transparent as a starting point so another test was created called
1348// directoryTest_KokkosSimple which demonstrates a more natural usage of the
1349// directory - that provides examples of how to use the directory which is
1350// independent of all the above stuff.
1351//
1352// Setting print_output true below will print a lot of information about the
1353// gid lists for update, remove, find, as well as the user data and associated
1354// lids after running find.
1355int runDirectoryTests(int narg, char **arg) {
1356 Tpetra::ScopeGuard tscope(&narg, &arg);
1357 Teuchos::RCP<const Teuchos::Comm<int> > comm =
1358 Teuchos::DefaultComm<int>::getComm();
1359
1360 // run the tests through a range of totalIds
1361 // we pick 0 and 1 and some low values for edge cases, then just
1362 // try to hit on some random spots
1363 std::vector<size_t> run_with_totalIds = {0, 1, 2, 3, 5, 27, 63, 456, 1093};
1364
1365 // note setting run_with_totalIds to something simpler may help to see what
1366 // the logs mean if print_output is turned on. For example:
1367 // run_with_totalIds = { 20 };
1368
1369 int err = 0;
1370
1371 const bool print_output = false; // noisy output with values for each gid
1372 const bool performance_test = false;
1373
1374 for(int run_with_local_ids = 0; run_with_local_ids <= 1; ++run_with_local_ids) {
1375
1376 for(size_t n = 0; n < run_with_totalIds.size(); ++n) {
1377
1378 print_proc_safe("Testing totalIds: " + std::to_string(run_with_totalIds[n]),
1379 comm, true);
1380
1381 comm->barrier();
1382
1383 TestManager manager(comm, run_with_totalIds[n], print_output,
1384 performance_test, run_with_local_ids ? true : false);
1385
1386 // loop the modes: Replace, Add, Aggregate and then do various tests on
1387 // each which vary gid_t, single or vector user type, single or multipe gid
1388 // some of the non-kokkos modes don't support everything and skip tests but
1389 // eventually they will all be deleted leaving only Kokkos
1390 for(int test_mode = 0; test_mode < TestMode_Max; ++test_mode) {
1391 // Aggregate mode is for vector user type only
1392 if(test_mode != Aggregate) {
1393 manager.run_single_user_single_gid<int, int, int>
1394 ("contiguous int", test_mode, 0, 1);
1395 manager.run_single_user_single_gid<int, int, int>
1396 ("non-contiguous int", test_mode, 20, 3);
1397
1398 manager.run_single_user_single_gid<long long, int, int>
1399 ("long long", test_mode, 200, 4);
1400 }
1401
1402 manager.run_vector_user_single_gid<int, int, std::vector<int>>
1403 ("contiguous int", TestMode::Aggregate, 0, 1);
1404 manager.run_vector_user_single_gid<int, int, std::vector<int>>
1405 ("non-contiguous int", TestMode::Aggregate, 20, 3);
1406 manager.run_vector_user_single_gid<long long, int, std::vector<int>>
1407 ("non-contiguous long long", TestMode::Aggregate, 200, 4);
1408
1409 // Aggregate mode is for vector user type only
1410 if(test_mode != Aggregate) {
1412 (GID_SET_LENGTH, LID_SET_LENGTH, "contiguous int", test_mode, 0, 1);
1414 (GID_SET_LENGTH, LID_SET_LENGTH, "non-contiguous int", test_mode, 20, 3);
1415 }
1416
1417 manager.run_vector_user_multiple_gid<gid_set_t, lid_set_t, std::vector<int>>
1418 (GID_SET_LENGTH, LID_SET_LENGTH, "contiguous int", test_mode, 0, 1);
1419
1420 if(!manager.did_all_pass()) {
1421 err = 1; // if we fail at some point just drop out
1422 }
1423 }
1424 }
1425 }
1426
1427 // Get the global err results so we fail properly if any proc failed
1428 int errGlobal;
1429 Teuchos::reduceAll<int>(*comm,Teuchos::REDUCE_SUM, err,
1430 Teuchos::outArg(errGlobal));
1431
1432 return errGlobal; // only 0 if all tests and all proc return 0
1433}
1434
1435} // namespace Zoltan2
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > user_t
Definition Metric.cpp:39
Traits class to handle conversions between gno_t/lno_t and TPL data types (e.g., ParMETIS's idx_t,...
std::vector< gid_t > update_gids
virtual std::string gid_to_string(gid_t gid) const =0
virtual std::string lid_to_string(lid_t lid) const =0
virtual int convert_gid_to_index(const gid_t &gid) const =0
bool did_test_pass() const
did_test_pass - did test pass
void print() const
detailed notes on update IDs, find IDs, etc
virtual void test()=0
int proc_remove_gid(gid_t gid, int rank) const
virtual bool evaluateTests() const =0
evaluateTests - determine if test worked
int proc_update_gid(gid_t gid, int rank) const
virtual size_t gid_seed_value(const gid_t &gid) const =0
void print_gids(const std::vector< gid_t > &printIds, std::string name) const
int getMode() const
getMode - Replace, Add, or Aggregate
virtual lid_t get_initial_lid(gid_t gid) const =0
virtual bool check_lid_equal(const lid_t &a, const lid_t &b) const =0
virtual void print_user_data() const =0
IDs(size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &test_name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
Construct IDs.
std::vector< user_t > update_user
Teuchos::RCP< const Teuchos::Comm< int > > comm
bool subset2(int index, int rank) const
virtual gid_t convert_index_to_gid(int index) const =0
std::vector< lid_t > find_lids
std::string get_mode_name() const
std::vector< user_t > find_user
void print_lids(const std::vector< lid_t > &printIds, std::string name) const
virtual std::string get_test_style() const =0
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
virtual void setup()
bool subset1(int index, int rank) const
std::vector< lid_t > update_lids
virtual void debug_print_subsets_and_decisions()
std::vector< gid_t > remove_gids
virtual void initialize_with_not_found_lid()
std::vector< gid_t > find_gids
int sharedCount(gid_t gid) const
virtual void print_lid_data() const
bool trueForAtLeastOneProc(int index, int rank) const
virtual user_t get_initial_user(gid_t gid, int rank) const =0
virtual void execute()
virtual lid_t get_not_found_lid() const =0
int proc_find_gid(gid_t gid, int rank) const
virtual user_t get_expected_user(gid_t gid) const =0
void printResultMessage(bool pass) const
const std::string & get_test_name() const
bool removedIDGlobally(gid_t gid) const
virtual void initialize_with_not_found_user()
virtual user_t get_not_found_user() const =0
virtual lid_t get_not_found_lid() const
virtual lid_t get_initial_lid(gid_t gid) const
virtual std::string gid_to_string(gid_t gid) const
virtual size_t gid_seed_value(const gid_t &gid) const
Multiple_GID(size_t gid_length_, size_t lid_length_)
virtual gid_t convert_index_to_gid(int index) const
virtual int convert_gid_to_index(const gid_t &gid) const
virtual bool check_lid_equal(const lid_t &a, const lid_t &b) const
virtual std::string lid_to_string(lid_t lid) const
virtual lid_t get_initial_lid(gid_t gid) const
virtual bool check_lid_equal(const lid_t &a, const lid_t &b) const
virtual gid_t convert_index_to_gid(int index) const
virtual lid_t get_not_found_lid() const
virtual size_t gid_seed_value(const gid_t &gid) const
virtual int convert_gid_to_index(const gid_t &gid) const
virtual std::string gid_to_string(gid_t gid) const
virtual std::string lid_to_string(lid_t lid) const
Single_User_Multiple_GID(size_t gid_length_, size_t lid_length_, size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
virtual std::string get_test_style() const
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
virtual std::string get_test_style() const
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
Single_User_Single_GID(size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
virtual user_t get_initial_user(gid_t gid, int rank) const
virtual user_t get_expected_user(gid_t gid) const
virtual bool evaluateTests() const
evaluateTests - determine if test worked
virtual user_t get_not_found_user() const
virtual void print_user_data() const
void run_single_user_multiple_gid(size_t gid_length_, size_t lid_length_, const std::string &name_, int mode_, size_t idBase_, int idStride_)
TestManager(Teuchos::RCP< const Teuchos::Comm< int > > comm_, int totalIds_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
void run_vector_user_multiple_gid(size_t gid_length_, size_t lid_length_, const std::string &name_, int mode_, size_t idBase_, int idStride_)
void run_vector_user_single_gid(const std::string &name_, int mode_, size_t idBase_, int idStride_)
void run_single_user_single_gid(const std::string &name_, int mode_, size_t idBase_, int idStride_)
virtual std::string get_test_style() const
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
Vector_User_Multiple_GID(size_t gid_length_, size_t lid_length_, size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
virtual std::string get_test_style() const
get_test_style the test is either vector user_t or single user_t the test is either multiple gids or ...
Vector_User_Single_GID(size_t totalIds_, size_t idBase_, size_t idStride_, Teuchos::RCP< const Teuchos::Comm< int > > &comm_, int mode_, const std::string &name_, bool print_detailed_output_, bool performance_test_, bool bUseLocalIDs_)
virtual void print_user_data() const
virtual bool evaluateTests() const
evaluateTests - determine if test worked
virtual user_t get_initial_user(gid_t gid, int rank) const
virtual user_t get_not_found_user() const
virtual size_t test_create_array_length(gid_t gid, int proc) const
virtual user_t get_expected_user(gid_t gid) const
#define LID_SET_LENGTH
#define NOT_FOUND_VALUE
#define GID_SET_LENGTH
Created by mbenlioglu on Aug 31, 2020.
void print_proc_safe(const std::string &message, Teuchos::RCP< const Teuchos::Comm< int > > comm, bool only_rank0=true)
static const std::string pass
int runDirectoryTests(int narg, char **arg)
int sub_gid[GID_SET_LENGTH]
int sub_lid[LID_SET_LENGTH]