xref: /petsc/src/sys/objects/device/tests/ex11.cxx (revision 017deb10d530c1b6d9744fcd772cd96c5fcd74f2)
174f7f6c6SJacob Faibussowitsch static const char help[] = "Tests PetscDeviceContextMarkIntentFromID().\n\n";
274f7f6c6SJacob Faibussowitsch 
374f7f6c6SJacob Faibussowitsch #include "petscdevicetestcommon.h"
474f7f6c6SJacob Faibussowitsch #include <petscviewer.h>
574f7f6c6SJacob Faibussowitsch 
674f7f6c6SJacob Faibussowitsch #include <petsc/private/cpp/type_traits.hpp>
774f7f6c6SJacob Faibussowitsch #include <petsc/private/cpp/array.hpp>
874f7f6c6SJacob Faibussowitsch 
974f7f6c6SJacob Faibussowitsch #include <cstdarg>       // std::va_list
1074f7f6c6SJacob Faibussowitsch #include <vector>        // std:vector
1174f7f6c6SJacob Faibussowitsch #include <unordered_map> // std::take_a_wild_guess
1274f7f6c6SJacob Faibussowitsch #include <algorithm>     // std::find
1374f7f6c6SJacob Faibussowitsch #include <iterator>      // std::distance, std::next
1474f7f6c6SJacob Faibussowitsch 
15f5c5fea7SStefano Zampini #include <petscmacros.h> // PETSC_CPP_VERSION
16f5c5fea7SStefano Zampini 
17f5c5fea7SStefano Zampini #if PETSC_CPP_VERSION > 14
1874f7f6c6SJacob Faibussowitsch struct Marker {
1974f7f6c6SJacob Faibussowitsch   PetscMemoryAccessMode mode{};
2074f7f6c6SJacob Faibussowitsch 
operator ()Marker21089fb57cSJacob Faibussowitsch   PetscErrorCode operator()(PetscDeviceContext dctx, PetscContainer cont) const noexcept
2274f7f6c6SJacob Faibussowitsch   {
2374f7f6c6SJacob Faibussowitsch     const auto    obj  = reinterpret_cast<PetscObject>(cont);
2474f7f6c6SJacob Faibussowitsch     PetscObjectId id   = 0;
2574f7f6c6SJacob Faibussowitsch     const char   *name = nullptr;
2674f7f6c6SJacob Faibussowitsch 
2774f7f6c6SJacob Faibussowitsch     PetscFunctionBegin;
2874f7f6c6SJacob Faibussowitsch     PetscCall(PetscObjectGetId(obj, &id));
2974f7f6c6SJacob Faibussowitsch     PetscCall(PetscObjectGetName(obj, &name));
3074f7f6c6SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, id, this->mode, name));
313ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
3274f7f6c6SJacob Faibussowitsch   }
3374f7f6c6SJacob Faibussowitsch };
3474f7f6c6SJacob Faibussowitsch 
3589e5c462SJacob Faibussowitsch static constexpr auto mem_read       = Marker{PETSC_MEMORY_ACCESS_READ};
3689e5c462SJacob Faibussowitsch static constexpr auto mem_write      = Marker{PETSC_MEMORY_ACCESS_WRITE};
3789e5c462SJacob Faibussowitsch static constexpr auto mem_read_write = Marker{PETSC_MEMORY_ACCESS_READ_WRITE};
3889e5c462SJacob Faibussowitsch static constexpr auto mark_funcs     = Petsc::util::make_array(mem_read, mem_write, mem_read_write);
3974f7f6c6SJacob Faibussowitsch 
MarkedObjectMapView(PetscViewer vwr,std::size_t nkeys,const PetscObjectId * keys,const PetscMemoryAccessMode * modes,const std::size_t * ndeps,const PetscEvent ** dependencies)4074f7f6c6SJacob Faibussowitsch static PetscErrorCode MarkedObjectMapView(PetscViewer vwr, std::size_t nkeys, const PetscObjectId *keys, const PetscMemoryAccessMode *modes, const std::size_t *ndeps, const PetscEvent **dependencies)
4174f7f6c6SJacob Faibussowitsch {
4274f7f6c6SJacob Faibussowitsch   PetscFunctionBegin;
4374f7f6c6SJacob Faibussowitsch   if (!vwr) PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &vwr));
4474f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerFlush(vwr));
4574f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerASCIIPushSynchronized(vwr));
4674f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "Marked Object Map:\n"));
4774f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerASCIIPushTab(vwr));
4874f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "size: %zu\n", nkeys));
4974f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "entries:\n"));
5074f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerASCIIPushTab(vwr));
5174f7f6c6SJacob Faibussowitsch   for (std::size_t i = 0; i < nkeys; ++i) {
5274f7f6c6SJacob Faibussowitsch     PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "id %" PetscInt64_FMT " -> {\n", keys[i]));
5374f7f6c6SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPushTab(vwr));
5474f7f6c6SJacob Faibussowitsch     PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "mode: %s\n", PetscMemoryAccessModeToString(modes[i])));
5574f7f6c6SJacob Faibussowitsch     PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "dependencies:\n"));
5674f7f6c6SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPushTab(vwr));
5774f7f6c6SJacob Faibussowitsch     for (std::size_t j = 0; j < ndeps[i]; ++j) {
5874f7f6c6SJacob Faibussowitsch       const auto event = dependencies[i][j];
5974f7f6c6SJacob Faibussowitsch 
607fca85f3SJacob Faibussowitsch       PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "event %zu {dtype: %s, dctx_id: %" PetscInt64_FMT ", dctx_state: %" PetscInt64_FMT ", data: %p, destroy: %p}\n", j, PetscDeviceTypes[event->dtype], event->dctx_id, event->dctx_state, event->data,
617fca85f3SJacob Faibussowitsch                                                    reinterpret_cast<void *>(event->destroy)));
6274f7f6c6SJacob Faibussowitsch     }
6374f7f6c6SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPopTab(vwr));
6474f7f6c6SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPopTab(vwr));
6574f7f6c6SJacob Faibussowitsch     PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "}\n"));
6674f7f6c6SJacob Faibussowitsch   }
6774f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerASCIIPopTab(vwr));
6874f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerASCIIPopTab(vwr));
6974f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerFlush(vwr));
7074f7f6c6SJacob Faibussowitsch   PetscCall(PetscViewerASCIIPopSynchronized(vwr));
713ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
7274f7f6c6SJacob Faibussowitsch }
7374f7f6c6SJacob Faibussowitsch 
CheckMarkedObjectMap_Private(PetscBool cond,const char cond_str[],MPI_Comm comm,PetscDeviceContext dctx,std::size_t nkeys,const PetscObjectId * keys,const PetscMemoryAccessMode * modes,const std::size_t * ndeps,const PetscEvent ** dependencies,const char * format,...)74fbccb6d4SPierre Jolivet PETSC_ATTRIBUTE_FORMAT(10, 11) static PetscErrorCode CheckMarkedObjectMap_Private(PetscBool cond, const char cond_str[], MPI_Comm comm, PetscDeviceContext dctx, std::size_t nkeys, const PetscObjectId *keys, const PetscMemoryAccessMode *modes, const std::size_t *ndeps, const PetscEvent **dependencies, const char *format, ...)
7574f7f6c6SJacob Faibussowitsch {
7674f7f6c6SJacob Faibussowitsch   PetscFunctionBegin;
7774f7f6c6SJacob Faibussowitsch   if (PetscUnlikely(!cond)) {
7874f7f6c6SJacob Faibussowitsch     std::array<char, 2048> buf;
7974f7f6c6SJacob Faibussowitsch     std::va_list           argp;
8074f7f6c6SJacob Faibussowitsch     std::size_t            len;
8174f7f6c6SJacob Faibussowitsch     PetscViewer            vwr;
8274f7f6c6SJacob Faibussowitsch 
8374f7f6c6SJacob Faibussowitsch     PetscCallCXX(buf.fill(0));
8474f7f6c6SJacob Faibussowitsch     va_start(argp, format);
8574f7f6c6SJacob Faibussowitsch     PetscCall(PetscVSNPrintf(buf.data(), buf.size(), format, &len, argp));
8674f7f6c6SJacob Faibussowitsch     va_end(argp);
8774f7f6c6SJacob Faibussowitsch     PetscCall(PetscViewerASCIIGetStdout(comm, &vwr));
8874f7f6c6SJacob Faibussowitsch     if (dctx) PetscCall(PetscDeviceContextView(dctx, vwr));
8974f7f6c6SJacob Faibussowitsch     PetscCall(MarkedObjectMapView(vwr, nkeys, keys, modes, ndeps, dependencies));
9074f7f6c6SJacob Faibussowitsch     SETERRQ(comm, PETSC_ERR_PLIB, "Condition '%s' failed, marked object map in corrupt state: %s", cond_str, buf.data());
9174f7f6c6SJacob Faibussowitsch   }
923ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
9374f7f6c6SJacob Faibussowitsch }
94f5c5fea7SStefano Zampini 
9574f7f6c6SJacob Faibussowitsch   #define CheckMarkedObjectMap(__cond__, ...) CheckMarkedObjectMap_Private((PetscBool)(!!(__cond__)), PetscStringize(__cond__), PETSC_COMM_SELF, dctx, nkeys, keys, modes, ndeps, const_cast<const PetscEvent **>(dependencies), __VA_ARGS__);
9674f7f6c6SJacob Faibussowitsch 
TestAllCombinations(PetscDeviceContext dctx,const std::vector<PetscContainer> & cont)9774f7f6c6SJacob Faibussowitsch static PetscErrorCode TestAllCombinations(PetscDeviceContext dctx, const std::vector<PetscContainer> &cont)
9874f7f6c6SJacob Faibussowitsch {
9974f7f6c6SJacob Faibussowitsch   std::vector<PetscObjectId> cont_ids;
10074f7f6c6SJacob Faibussowitsch   PetscObjectId              dctx_id;
10174f7f6c6SJacob Faibussowitsch   PetscDeviceType            dtype;
10274f7f6c6SJacob Faibussowitsch 
10374f7f6c6SJacob Faibussowitsch   PetscFunctionBegin;
10474f7f6c6SJacob Faibussowitsch   PetscCallCXX(cont_ids.reserve(cont.size()));
10574f7f6c6SJacob Faibussowitsch   for (auto &&c : cont) {
10674f7f6c6SJacob Faibussowitsch     PetscObjectId id;
10774f7f6c6SJacob Faibussowitsch 
10874f7f6c6SJacob Faibussowitsch     PetscCall(PetscObjectGetId((PetscObject)c, &id));
10974f7f6c6SJacob Faibussowitsch     PetscCallCXX(cont_ids.emplace_back(id));
11074f7f6c6SJacob Faibussowitsch   }
11174f7f6c6SJacob Faibussowitsch   PetscCall(PetscObjectGetId(PetscObjectCast(dctx), &dctx_id));
11274f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype));
11374f7f6c6SJacob Faibussowitsch   for (auto &&func_i : mark_funcs) {
11474f7f6c6SJacob Faibussowitsch     for (auto &&func_j : mark_funcs) {
11574f7f6c6SJacob Faibussowitsch       for (auto it = cont.cbegin(), next = std::next(it); it != cont.cend(); ++it, ++next) {
11674f7f6c6SJacob Faibussowitsch         std::vector<int>       found_keys;
11774f7f6c6SJacob Faibussowitsch         std::size_t            nkeys;
11874f7f6c6SJacob Faibussowitsch         PetscObjectId         *keys;
11974f7f6c6SJacob Faibussowitsch         PetscMemoryAccessMode *modes;
12074f7f6c6SJacob Faibussowitsch         std::size_t           *ndeps;
12174f7f6c6SJacob Faibussowitsch         PetscEvent           **dependencies;
12274f7f6c6SJacob Faibussowitsch 
12374f7f6c6SJacob Faibussowitsch         if (next >= cont.cend()) next = cont.cbegin();
12474f7f6c6SJacob Faibussowitsch         PetscCall(func_i(dctx, *it));
12574f7f6c6SJacob Faibussowitsch         PetscCall(func_j(dctx, *next));
12674f7f6c6SJacob Faibussowitsch         PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies));
12774f7f6c6SJacob Faibussowitsch         PetscCallCXX(found_keys.resize(nkeys));
12874f7f6c6SJacob Faibussowitsch         {
12974f7f6c6SJacob Faibussowitsch           // The underlying marked object map is *unordered*, and hence the order in which we
13074f7f6c6SJacob Faibussowitsch           // get the keys is not necessarily the same as the order of operations. This is
13174f7f6c6SJacob Faibussowitsch           // confounded by the fact that k and knext are not necessarily "linear", i.e. k could
13274f7f6c6SJacob Faibussowitsch           // be 2 while knext is 0. So we need to map these back to linear space so we can loop
13374f7f6c6SJacob Faibussowitsch           // over them.
13474f7f6c6SJacob Faibussowitsch           const auto keys_end           = keys + nkeys;
13574f7f6c6SJacob Faibussowitsch           const auto num_expected_keys  = std::min(cont.size(), static_cast<std::size_t>(2));
13674f7f6c6SJacob Faibussowitsch           const auto check_applied_mode = [&](PetscContainer container, PetscMemoryAccessMode mode) {
13774f7f6c6SJacob Faibussowitsch             std::ptrdiff_t key_idx = 0;
13874f7f6c6SJacob Faibussowitsch             PetscObjectId  actual_key;
13974f7f6c6SJacob Faibussowitsch 
14074f7f6c6SJacob Faibussowitsch             PetscFunctionBegin;
14174f7f6c6SJacob Faibussowitsch             PetscCall(PetscObjectGetId((PetscObject)container, &actual_key));
14274f7f6c6SJacob Faibussowitsch             // search the list of keys from the map for the selected key
14374f7f6c6SJacob Faibussowitsch             key_idx = std::distance(keys, std::find(keys, keys_end, actual_key));
14474f7f6c6SJacob Faibussowitsch             PetscCheck(key_idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Key index %" PetscCount_FMT " < 0, this indicates keys_begin > keys_end?", key_idx);
14574f7f6c6SJacob Faibussowitsch             found_keys[key_idx]++;
14674f7f6c6SJacob Faibussowitsch             PetscCall(CheckMarkedObjectMap(key_idx < std::distance(keys, keys_end), "marked object map could not find expected key %" PetscInt64_FMT, actual_key));
14774f7f6c6SJacob Faibussowitsch             // OK found it, now check the rest of the entries are as we expect them to be
14874f7f6c6SJacob Faibussowitsch             PetscCall(CheckMarkedObjectMap(modes[key_idx] == mode, "unexpected mode %s, expected %s", PetscMemoryAccessModeToString(modes[key_idx]), PetscMemoryAccessModeToString(mode)));
14974f7f6c6SJacob Faibussowitsch             PetscCall(CheckMarkedObjectMap(ndeps[key_idx] == 1, "unexpected number of dependencies %zu, expected 1", ndeps[key_idx]));
15074f7f6c6SJacob Faibussowitsch             PetscCall(CheckMarkedObjectMap(dependencies[key_idx][0]->dtype == dtype, "unexpected device type on event: %s, expected %s", PetscDeviceTypes[dependencies[key_idx][0]->dtype], PetscDeviceTypes[dtype]));
1513ba16761SJacob Faibussowitsch             PetscFunctionReturn(PETSC_SUCCESS);
15274f7f6c6SJacob Faibussowitsch           };
15374f7f6c6SJacob Faibussowitsch 
15474f7f6c6SJacob Faibussowitsch           // if it == next, then even though we might num_expected_keys keys we never "look
15574f7f6c6SJacob Faibussowitsch           // for" the missing key
15613bcc0bdSJacob Faibussowitsch           PetscCheck(cont.size() == 1 || it != next, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Test assumes different inputs, otherwise key check may fail (cont.size(): %zu, it != next: %s)", cont.size(), it != next ? "true" : "false");
15774f7f6c6SJacob Faibussowitsch           PetscCall(CheckMarkedObjectMap(nkeys == num_expected_keys, "marked object map has %zu keys expected %zu", nkeys, num_expected_keys));
15874f7f6c6SJacob Faibussowitsch           // check that each function properly applied its mode, it == next if cont.size() = 1,
15974f7f6c6SJacob Faibussowitsch           // i.e. testing identity
16074f7f6c6SJacob Faibussowitsch           if (it != next) PetscCall(check_applied_mode(*it, func_i.mode));
16174f7f6c6SJacob Faibussowitsch           PetscCall(check_applied_mode(*next, func_j.mode));
16274f7f6c6SJacob Faibussowitsch         }
16374f7f6c6SJacob Faibussowitsch         // Check that the map contained only keys we were looking for. Any extra keys will have
16474f7f6c6SJacob Faibussowitsch         // zero find count
16574f7f6c6SJacob Faibussowitsch         for (auto it = found_keys.cbegin(); it != found_keys.cend(); ++it) PetscCall(CheckMarkedObjectMap(*it > 0, "Marked Object Map has extra object entry: id %" PetscInt64_FMT, keys[std::distance(found_keys.cbegin(), it)]));
16674f7f6c6SJacob Faibussowitsch 
16774f7f6c6SJacob Faibussowitsch         PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies));
16874f7f6c6SJacob Faibussowitsch 
16974f7f6c6SJacob Faibussowitsch         PetscCall(PetscDeviceContextSynchronize(dctx));
17074f7f6c6SJacob Faibussowitsch         PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies));
17174f7f6c6SJacob Faibussowitsch         PetscCall(CheckMarkedObjectMap(nkeys == 0, "synchronizing device context did not empty dependency map, have %zu keys", nkeys));
17274f7f6c6SJacob Faibussowitsch         PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies));
17374f7f6c6SJacob Faibussowitsch       }
17474f7f6c6SJacob Faibussowitsch     }
17574f7f6c6SJacob Faibussowitsch   }
17674f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx));
1773ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
17874f7f6c6SJacob Faibussowitsch }
17974f7f6c6SJacob Faibussowitsch 
18074f7f6c6SJacob Faibussowitsch template <typename... T>
make_map_entry(PetscObjectId id,PetscMemoryAccessMode mode,T &&...dctxs)18174f7f6c6SJacob Faibussowitsch PETSC_NODISCARD static std::pair<PetscObjectId, std::pair<PetscMemoryAccessMode, std::vector<PetscDeviceContext>>> make_map_entry(PetscObjectId id, PetscMemoryAccessMode mode, T &&...dctxs)
18274f7f6c6SJacob Faibussowitsch {
18374f7f6c6SJacob Faibussowitsch   return {
18474f7f6c6SJacob Faibussowitsch     id, {mode, {std::forward<T>(dctxs)...}}
18574f7f6c6SJacob Faibussowitsch   };
18674f7f6c6SJacob Faibussowitsch }
18774f7f6c6SJacob Faibussowitsch 
CheckMapEqual(std::unordered_map<PetscObjectId,std::pair<PetscMemoryAccessMode,std::vector<PetscDeviceContext>>> expected_map)18874f7f6c6SJacob Faibussowitsch static PetscErrorCode CheckMapEqual(std::unordered_map<PetscObjectId, std::pair<PetscMemoryAccessMode, std::vector<PetscDeviceContext>>> expected_map)
18974f7f6c6SJacob Faibussowitsch {
19074f7f6c6SJacob Faibussowitsch   std::size_t            nkeys;
19174f7f6c6SJacob Faibussowitsch   PetscObjectId         *keys;
19274f7f6c6SJacob Faibussowitsch   PetscMemoryAccessMode *modes;
19374f7f6c6SJacob Faibussowitsch   std::size_t           *ndeps;
19474f7f6c6SJacob Faibussowitsch   PetscEvent           **dependencies;
19574f7f6c6SJacob Faibussowitsch   PetscDeviceContext     dctx = nullptr;
19674f7f6c6SJacob Faibussowitsch 
19774f7f6c6SJacob Faibussowitsch   PetscFunctionBegin;
19874f7f6c6SJacob Faibussowitsch   PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies));
19974f7f6c6SJacob Faibussowitsch   {
20074f7f6c6SJacob Faibussowitsch     const auto key_end = keys + nkeys;
20174f7f6c6SJacob Faibussowitsch     auto       mode_it = modes;
20274f7f6c6SJacob Faibussowitsch     auto       ndep_it = ndeps;
20374f7f6c6SJacob Faibussowitsch     auto       dep_it  = dependencies;
20474f7f6c6SJacob Faibussowitsch 
20574f7f6c6SJacob Faibussowitsch     for (auto key_it = keys; key_it != key_end; ++key_it, ++mode_it, ++ndep_it, ++dep_it) {
20674f7f6c6SJacob Faibussowitsch       const auto found_it = expected_map.find(*key_it);
20774f7f6c6SJacob Faibussowitsch 
20874f7f6c6SJacob Faibussowitsch       PetscCall(CheckMarkedObjectMap(found_it != expected_map.cend(), "marked object map did not contain key %" PetscInt64_FMT, *key_it));
20974f7f6c6SJacob Faibussowitsch       {
21074f7f6c6SJacob Faibussowitsch         // must do these here since found_it may be expected_map.cend()
21174f7f6c6SJacob Faibussowitsch         const auto &expected_mode  = found_it->second.first;
21274f7f6c6SJacob Faibussowitsch         const auto &expected_dctxs = found_it->second.second;
21374f7f6c6SJacob Faibussowitsch         auto        sub_dep_it     = *dep_it;
21474f7f6c6SJacob Faibussowitsch 
21574f7f6c6SJacob Faibussowitsch         PetscCall(CheckMarkedObjectMap(expected_mode == *mode_it, "unexpected mode %s, expected %s", PetscMemoryAccessModeToString(expected_mode), PetscMemoryAccessModeToString(*mode_it)));
21674f7f6c6SJacob Faibussowitsch         PetscCall(CheckMarkedObjectMap(expected_dctxs.size() == *ndep_it, "unexpected number of dependencies %zu, expected %zu", *ndep_it, expected_dctxs.size()));
21774f7f6c6SJacob Faibussowitsch         // purposefully hide "dctx" with the loop variable, so we get more detailed output in
21874f7f6c6SJacob Faibussowitsch         // the error message
21974f7f6c6SJacob Faibussowitsch         for (auto &&dctx : expected_dctxs) {
22074f7f6c6SJacob Faibussowitsch           const auto      event = *sub_dep_it;
22174f7f6c6SJacob Faibussowitsch           PetscDeviceType dtype;
22274f7f6c6SJacob Faibussowitsch           PetscObjectId   id;
22374f7f6c6SJacob Faibussowitsch 
22474f7f6c6SJacob Faibussowitsch           PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype));
22574f7f6c6SJacob Faibussowitsch           PetscCall(PetscObjectGetId(PetscObjectCast(dctx), &id));
22674f7f6c6SJacob Faibussowitsch           PetscCall(CheckMarkedObjectMap(event->dtype == dtype, "unexpected device type on event: %s, expected %s", PetscDeviceTypes[event->dtype], PetscDeviceTypes[dtype]));
22774f7f6c6SJacob Faibussowitsch           PetscCall(CheckMarkedObjectMap(event->dctx_id == id, "unexpected dctx id on event: %" PetscInt64_FMT ", expected %" PetscInt64_FMT, event->dctx_id, id));
22874f7f6c6SJacob Faibussowitsch           ++sub_dep_it;
22974f7f6c6SJacob Faibussowitsch         }
23074f7f6c6SJacob Faibussowitsch       }
23174f7f6c6SJacob Faibussowitsch       // remove the found iterator from the map, this ensure we either run out of map (which is
232aaa8cc7dSPierre Jolivet       // caught by the first check in the loop), or we run out of keys to check, which is
23374f7f6c6SJacob Faibussowitsch       // caught in the end of the loop
23474f7f6c6SJacob Faibussowitsch       PetscCallCXX(expected_map.erase(found_it));
23574f7f6c6SJacob Faibussowitsch     }
23674f7f6c6SJacob Faibussowitsch   }
23774f7f6c6SJacob Faibussowitsch   PetscCall(CheckMarkedObjectMap(expected_map.empty(), "Not all keys in marked object map accounted for!"));
23874f7f6c6SJacob Faibussowitsch   PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies));
2393ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
24074f7f6c6SJacob Faibussowitsch }
24174f7f6c6SJacob Faibussowitsch 
main(int argc,char * argv[])24274f7f6c6SJacob Faibussowitsch int main(int argc, char *argv[])
24374f7f6c6SJacob Faibussowitsch {
24474f7f6c6SJacob Faibussowitsch   PetscContainer     x, y, z;
24574f7f6c6SJacob Faibussowitsch   PetscObjectId      x_id, y_id, z_id;
24674f7f6c6SJacob Faibussowitsch   PetscDeviceContext dctx_a, dctx_b, dctx_c;
24774f7f6c6SJacob Faibussowitsch   auto               container_view   = PETSC_FALSE;
24874f7f6c6SJacob Faibussowitsch   const auto         create_container = [&](PetscContainer *c, const char name[], PetscObjectId *id) {
24974f7f6c6SJacob Faibussowitsch     PetscFunctionBegin;
25074f7f6c6SJacob Faibussowitsch     PetscCall(PetscContainerCreate(PETSC_COMM_WORLD, c));
251f4f49eeaSPierre Jolivet     PetscCall(PetscObjectSetName((PetscObject)*c, name));
252f4f49eeaSPierre Jolivet     PetscCall(PetscObjectGetId((PetscObject)*c, id));
25374f7f6c6SJacob Faibussowitsch     if (container_view) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Container '%s' -> id %" PetscInt64_FMT "\n", name, *id));
2543ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
25574f7f6c6SJacob Faibussowitsch   };
25674f7f6c6SJacob Faibussowitsch   const auto sync_all = [&] {
25774f7f6c6SJacob Faibussowitsch     PetscFunctionBegin;
25874f7f6c6SJacob Faibussowitsch     for (auto &&ctx : {dctx_a, dctx_b, dctx_c}) PetscCall(PetscDeviceContextSynchronize(ctx));
2593ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
26074f7f6c6SJacob Faibussowitsch   };
26174f7f6c6SJacob Faibussowitsch 
26274f7f6c6SJacob Faibussowitsch   PetscFunctionBeginUser;
26374f7f6c6SJacob Faibussowitsch   PetscCall(PetscInitialize(&argc, &argv, nullptr, help));
26474f7f6c6SJacob Faibussowitsch 
26574f7f6c6SJacob Faibussowitsch   PetscOptionsBegin(PETSC_COMM_WORLD, nullptr, "Test Options", "Sys");
26674f7f6c6SJacob Faibussowitsch   PetscCall(PetscOptionsBool("-container_view", "View container names and ID's", nullptr, container_view, &container_view, nullptr));
26774f7f6c6SJacob Faibussowitsch   PetscOptionsEnd();
26874f7f6c6SJacob Faibussowitsch 
26974f7f6c6SJacob Faibussowitsch   PetscCall(create_container(&x, "x", &x_id));
27074f7f6c6SJacob Faibussowitsch   PetscCall(create_container(&y, "y", &y_id));
27174f7f6c6SJacob Faibussowitsch   PetscCall(create_container(&z, "z", &z_id));
27274f7f6c6SJacob Faibussowitsch 
27374f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextCreate(&dctx_a));
27474f7f6c6SJacob Faibussowitsch   PetscCall(PetscObjectSetName(PetscObjectCast(dctx_a), "dctx_a"));
275d9acb416SHong Zhang   PetscCall(PetscDeviceContextSetStreamType(dctx_a, PETSC_STREAM_DEFAULT));
27674f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSetFromOptions(PETSC_COMM_WORLD, dctx_a));
27774f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextDuplicate(dctx_a, &dctx_b));
27874f7f6c6SJacob Faibussowitsch   PetscCall(PetscObjectSetName(PetscObjectCast(dctx_b), "dctx_b"));
27974f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextDuplicate(dctx_a, &dctx_c));
28074f7f6c6SJacob Faibussowitsch   PetscCall(PetscObjectSetName(PetscObjectCast(dctx_c), "dctx_c"));
28174f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextViewFromOptions(dctx_a, nullptr, "-dctx_a_view"));
28274f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextViewFromOptions(dctx_b, nullptr, "-dctx_b_view"));
28374f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextViewFromOptions(dctx_c, nullptr, "-dctx_c_view"));
28474f7f6c6SJacob Faibussowitsch 
28574f7f6c6SJacob Faibussowitsch   // ensure they are all idle
28674f7f6c6SJacob Faibussowitsch   PetscCall(sync_all());
28774f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
28874f7f6c6SJacob Faibussowitsch 
28974f7f6c6SJacob Faibussowitsch   // do the bulk combination tests, these test only the very basic combinations for simple
29074f7f6c6SJacob Faibussowitsch   // correctness
29174f7f6c6SJacob Faibussowitsch   PetscCall(TestAllCombinations(dctx_a, {x}));
29274f7f6c6SJacob Faibussowitsch   PetscCall(TestAllCombinations(dctx_a, {x, y, z}));
29374f7f6c6SJacob Faibussowitsch 
29474f7f6c6SJacob Faibussowitsch   // Now do some specific tests, these should test more complicated scenarios. First and
29574f7f6c6SJacob Faibussowitsch   // foremost, ensure they are all idle, and that it does not change the map
29674f7f6c6SJacob Faibussowitsch   PetscCall(sync_all());
29774f7f6c6SJacob Faibussowitsch   // Map should be empty
29874f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
29974f7f6c6SJacob Faibussowitsch 
30074f7f6c6SJacob Faibussowitsch   // Syncing again shouldn't magically fill the map back up
30174f7f6c6SJacob Faibussowitsch   PetscCall(sync_all());
30274f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
30374f7f6c6SJacob Faibussowitsch 
30474f7f6c6SJacob Faibussowitsch   const auto test_multiple_readers = [&](std::array<PetscDeviceContext, 2> readers, std::size_t sync_idx) {
30574f7f6c6SJacob Faibussowitsch     // the reader which synchronizes
30674f7f6c6SJacob Faibussowitsch     const auto sync_reader = readers[sync_idx];
30774f7f6c6SJacob Faibussowitsch     // the reader that will remain in the map after sync_reader synchronizes
30874f7f6c6SJacob Faibussowitsch     const auto remain_idx    = sync_idx + 1 >= readers.size() ? 0 : sync_idx + 1;
30974f7f6c6SJacob Faibussowitsch     const auto remain_reader = readers[remain_idx];
31074f7f6c6SJacob Faibussowitsch 
31174f7f6c6SJacob Faibussowitsch     PetscFunctionBegin;
31289e5c462SJacob Faibussowitsch     for (auto &&ctx : readers) PetscCall(mem_read(ctx, x));
31389e5c462SJacob Faibussowitsch     for (auto &&ctx : readers) PetscCall(mem_read(ctx, y));
31474f7f6c6SJacob Faibussowitsch     PetscCall(CheckMapEqual({
31574f7f6c6SJacob Faibussowitsch       make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, readers[0], readers[1]),
31674f7f6c6SJacob Faibussowitsch       make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, readers[0], readers[1]),
31774f7f6c6SJacob Faibussowitsch     }));
31874f7f6c6SJacob Faibussowitsch     // synchronizing sync_reader should remove it from the dependency list -- but leave remain_reader
31974f7f6c6SJacob Faibussowitsch     // intact
32074f7f6c6SJacob Faibussowitsch     PetscCall(PetscDeviceContextSynchronize(sync_reader));
32174f7f6c6SJacob Faibussowitsch     PetscCall(CheckMapEqual({
32274f7f6c6SJacob Faibussowitsch       make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, remain_reader),
32374f7f6c6SJacob Faibussowitsch       make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, remain_reader),
32474f7f6c6SJacob Faibussowitsch     }));
32574f7f6c6SJacob Faibussowitsch     PetscCall(PetscDeviceContextSynchronize(remain_reader));
32674f7f6c6SJacob Faibussowitsch     PetscCall(CheckMapEqual({}));
3273ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
32874f7f6c6SJacob Faibussowitsch   };
32974f7f6c6SJacob Faibussowitsch 
330aaa8cc7dSPierre Jolivet   // Test that multiple readers can simultaneously read -- even if one of them is synchronized
33174f7f6c6SJacob Faibussowitsch   PetscCall(test_multiple_readers({dctx_a, dctx_b}, 0));
33274f7f6c6SJacob Faibussowitsch   PetscCall(test_multiple_readers({dctx_a, dctx_b}, 1));
33374f7f6c6SJacob Faibussowitsch 
33474f7f6c6SJacob Faibussowitsch   // Test that sync of unrelated ctx does not affect the map
33589e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_a, x));
33689e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_b, y));
33774f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_c));
33874f7f6c6SJacob Faibussowitsch   // clang-format off
33974f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({
34074f7f6c6SJacob Faibussowitsch     make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a),
34174f7f6c6SJacob Faibussowitsch     make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, dctx_b)
34274f7f6c6SJacob Faibussowitsch   }));
34374f7f6c6SJacob Faibussowitsch   // clang-format on
34474f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_a));
34574f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_b));
34674f7f6c6SJacob Faibussowitsch   // Now the map is empty again
34774f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
34874f7f6c6SJacob Faibussowitsch 
34974f7f6c6SJacob Faibussowitsch   // Test another context writing over two reads
35089e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_a, x));
35189e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_b, x));
35274f7f6c6SJacob Faibussowitsch   // C writing should kick out both A and B
35389e5c462SJacob Faibussowitsch   PetscCall(mem_write(dctx_c, x));
35474f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_c)}));
35574f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_c));
35674f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
35774f7f6c6SJacob Faibussowitsch 
35874f7f6c6SJacob Faibussowitsch   // Test that write and synchronize does not interfere with unrelated read
35989e5c462SJacob Faibussowitsch   PetscCall(mem_read_write(dctx_a, x));
36089e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_a, y));
36189e5c462SJacob Faibussowitsch   PetscCall(mem_read_write(dctx_b, x));
36289e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_b, y));
36374f7f6c6SJacob Faibussowitsch   // Synchronizing B here must clear everything *but* A's read on Y!
36474f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_b));
36574f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, dctx_a)}));
36674f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_a));
36774f7f6c6SJacob Faibussowitsch   // Now the map is empty again
36874f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
36974f7f6c6SJacob Faibussowitsch 
37074f7f6c6SJacob Faibussowitsch   // Test that implicit stream-dependencies are properly tracked
37189e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_a, x));
37289e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_b, y));
37374f7f6c6SJacob Faibussowitsch   // A waits for B
37474f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextWaitForContext(dctx_a, dctx_b));
37574f7f6c6SJacob Faibussowitsch   // Because A waits on B, synchronizing A implicitly implies B read must have finished so the
37674f7f6c6SJacob Faibussowitsch   // map must be empty
37774f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_a));
37874f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
37974f7f6c6SJacob Faibussowitsch 
38089e5c462SJacob Faibussowitsch   PetscCall(mem_write(dctx_a, x));
38174f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_a)}));
38274f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextWaitForContext(dctx_b, dctx_a));
38374f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextWaitForContext(dctx_c, dctx_b));
38474f7f6c6SJacob Faibussowitsch   // We have created the chain C -> B -> A, so synchronizing C should trickle down to synchronize and
38574f7f6c6SJacob Faibussowitsch   // remove A from the map
38674f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_c));
38774f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
38874f7f6c6SJacob Faibussowitsch 
38974f7f6c6SJacob Faibussowitsch   // Test that superfluous stream-dependencies are properly ignored
39089e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_a, x));
39189e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_b, y));
39274f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextWaitForContext(dctx_c, dctx_b));
39374f7f6c6SJacob Faibussowitsch   // C waited on B, so synchronizing C should remove B from the map but *not* remove A
39474f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_c));
39574f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a)}));
39674f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_a));
39774f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
39874f7f6c6SJacob Faibussowitsch 
39974f7f6c6SJacob Faibussowitsch   // Test that read->write correctly wipes out the map
40089e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_a, x));
40189e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_b, x));
40289e5c462SJacob Faibussowitsch   PetscCall(mem_read(dctx_c, x));
40374f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a, dctx_b, dctx_c)}));
40489e5c462SJacob Faibussowitsch   PetscCall(mem_write(dctx_a, x));
40574f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_a)}));
40674f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx_a));
40774f7f6c6SJacob Faibussowitsch   PetscCall(CheckMapEqual({}));
40874f7f6c6SJacob Faibussowitsch 
40974f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextDestroy(&dctx_a));
41074f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextDestroy(&dctx_b));
41174f7f6c6SJacob Faibussowitsch   PetscCall(PetscDeviceContextDestroy(&dctx_c));
41274f7f6c6SJacob Faibussowitsch 
41374f7f6c6SJacob Faibussowitsch   PetscCall(PetscContainerDestroy(&x));
41474f7f6c6SJacob Faibussowitsch   PetscCall(PetscContainerDestroy(&y));
41574f7f6c6SJacob Faibussowitsch   PetscCall(PetscContainerDestroy(&z));
41674f7f6c6SJacob Faibussowitsch   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "EXIT_SUCCESS\n"));
41774f7f6c6SJacob Faibussowitsch   PetscCall(PetscFinalize());
41874f7f6c6SJacob Faibussowitsch   return 0;
41974f7f6c6SJacob Faibussowitsch }
420f5c5fea7SStefano Zampini #else // PETSC_CPP_VERSION > 11
main(int argc,char * argv[])421f5c5fea7SStefano Zampini int main(int argc, char *argv[])
422f5c5fea7SStefano Zampini {
423f5c5fea7SStefano Zampini   PetscFunctionBeginUser;
424f5c5fea7SStefano Zampini   PetscCall(PetscInitialize(&argc, &argv, nullptr, help));
425f5c5fea7SStefano Zampini   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "EXIT_SUCCESS\n"));
426f5c5fea7SStefano Zampini   PetscCall(PetscFinalize());
427f5c5fea7SStefano Zampini   return 0;
428f5c5fea7SStefano Zampini }
429f5c5fea7SStefano Zampini #endif
43074f7f6c6SJacob Faibussowitsch 
43174f7f6c6SJacob Faibussowitsch /*TEST
43274f7f6c6SJacob Faibussowitsch 
43374f7f6c6SJacob Faibussowitsch   testset:
434*27d4e10aSPierre Jolivet     requires: defined(PETSC_DEVICELANGUAGE_CXX)
43561c8d4edSPierre Jolivet     output_file: output/ExitSuccess.out
43674f7f6c6SJacob Faibussowitsch     test:
43774f7f6c6SJacob Faibussowitsch       requires: !device
43874f7f6c6SJacob Faibussowitsch       suffix: host_no_device
43974f7f6c6SJacob Faibussowitsch     test:
44074f7f6c6SJacob Faibussowitsch       requires: device
44174f7f6c6SJacob Faibussowitsch       args: -default_device_type host
44274f7f6c6SJacob Faibussowitsch       suffix: host_with_device
44374f7f6c6SJacob Faibussowitsch     test:
44474f7f6c6SJacob Faibussowitsch       requires: cuda
44574f7f6c6SJacob Faibussowitsch       args: -default_device_type cuda
44674f7f6c6SJacob Faibussowitsch       suffix: cuda
44774f7f6c6SJacob Faibussowitsch     test:
44874f7f6c6SJacob Faibussowitsch       requires: hip
44974f7f6c6SJacob Faibussowitsch       args: -default_device_type hip
45074f7f6c6SJacob Faibussowitsch       suffix: hip
45174f7f6c6SJacob Faibussowitsch     test:
45274f7f6c6SJacob Faibussowitsch       requires: sycl
45374f7f6c6SJacob Faibussowitsch       args: -default_device_type sycl
45474f7f6c6SJacob Faibussowitsch       suffix: sycl
45574f7f6c6SJacob Faibussowitsch 
45674f7f6c6SJacob Faibussowitsch TEST*/
457