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 1574f7f6c6SJacob Faibussowitsch struct Marker { 1674f7f6c6SJacob Faibussowitsch PetscMemoryAccessMode mode{}; 1774f7f6c6SJacob Faibussowitsch 18089fb57cSJacob Faibussowitsch PetscErrorCode operator()(PetscDeviceContext dctx, PetscContainer cont) const noexcept 1974f7f6c6SJacob Faibussowitsch { 2074f7f6c6SJacob Faibussowitsch const auto obj = reinterpret_cast<PetscObject>(cont); 2174f7f6c6SJacob Faibussowitsch PetscObjectId id = 0; 2274f7f6c6SJacob Faibussowitsch const char *name = nullptr; 2374f7f6c6SJacob Faibussowitsch 2474f7f6c6SJacob Faibussowitsch PetscFunctionBegin; 2574f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectGetId(obj, &id)); 2674f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectGetName(obj, &name)); 2774f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, id, this->mode, name)); 283ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 2974f7f6c6SJacob Faibussowitsch } 3074f7f6c6SJacob Faibussowitsch }; 3174f7f6c6SJacob Faibussowitsch 3289e5c462SJacob Faibussowitsch static constexpr auto mem_read = Marker{PETSC_MEMORY_ACCESS_READ}; 3389e5c462SJacob Faibussowitsch static constexpr auto mem_write = Marker{PETSC_MEMORY_ACCESS_WRITE}; 3489e5c462SJacob Faibussowitsch static constexpr auto mem_read_write = Marker{PETSC_MEMORY_ACCESS_READ_WRITE}; 3589e5c462SJacob Faibussowitsch static constexpr auto mark_funcs = Petsc::util::make_array(mem_read, mem_write, mem_read_write); 3674f7f6c6SJacob Faibussowitsch 3774f7f6c6SJacob Faibussowitsch static PetscErrorCode MarkedObjectMapView(PetscViewer vwr, std::size_t nkeys, const PetscObjectId *keys, const PetscMemoryAccessMode *modes, const std::size_t *ndeps, const PetscEvent **dependencies) 3874f7f6c6SJacob Faibussowitsch { 3974f7f6c6SJacob Faibussowitsch PetscFunctionBegin; 4074f7f6c6SJacob Faibussowitsch if (!vwr) PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &vwr)); 4174f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerFlush(vwr)); 4274f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPushSynchronized(vwr)); 4374f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "Marked Object Map:\n")); 4474f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPushTab(vwr)); 4574f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "size: %zu\n", nkeys)); 4674f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "entries:\n")); 4774f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPushTab(vwr)); 4874f7f6c6SJacob Faibussowitsch for (std::size_t i = 0; i < nkeys; ++i) { 4974f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "id %" PetscInt64_FMT " -> {\n", keys[i])); 5074f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPushTab(vwr)); 5174f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "mode: %s\n", PetscMemoryAccessModeToString(modes[i]))); 5274f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "dependencies:\n")); 5374f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPushTab(vwr)); 5474f7f6c6SJacob Faibussowitsch for (std::size_t j = 0; j < ndeps[i]; ++j) { 5574f7f6c6SJacob Faibussowitsch const auto event = dependencies[i][j]; 5674f7f6c6SJacob Faibussowitsch 577fca85f3SJacob 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, 587fca85f3SJacob Faibussowitsch reinterpret_cast<void *>(event->destroy))); 5974f7f6c6SJacob Faibussowitsch } 6074f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPopTab(vwr)); 6174f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPopTab(vwr)); 6274f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "}\n")); 6374f7f6c6SJacob Faibussowitsch } 6474f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPopTab(vwr)); 6574f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPopTab(vwr)); 6674f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerFlush(vwr)); 6774f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIPopSynchronized(vwr)); 683ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 6974f7f6c6SJacob Faibussowitsch } 7074f7f6c6SJacob Faibussowitsch 7174f7f6c6SJacob Faibussowitsch PETSC_ATTRIBUTE_FORMAT(10, 11) 7274f7f6c6SJacob Faibussowitsch 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, ...) 7374f7f6c6SJacob Faibussowitsch { 7474f7f6c6SJacob Faibussowitsch PetscFunctionBegin; 7574f7f6c6SJacob Faibussowitsch if (PetscUnlikely(!cond)) { 7674f7f6c6SJacob Faibussowitsch std::array<char, 2048> buf; 7774f7f6c6SJacob Faibussowitsch std::va_list argp; 7874f7f6c6SJacob Faibussowitsch std::size_t len; 7974f7f6c6SJacob Faibussowitsch PetscViewer vwr; 8074f7f6c6SJacob Faibussowitsch 8174f7f6c6SJacob Faibussowitsch PetscCallCXX(buf.fill(0)); 8274f7f6c6SJacob Faibussowitsch va_start(argp, format); 8374f7f6c6SJacob Faibussowitsch PetscCall(PetscVSNPrintf(buf.data(), buf.size(), format, &len, argp)); 8474f7f6c6SJacob Faibussowitsch va_end(argp); 8574f7f6c6SJacob Faibussowitsch PetscCall(PetscViewerASCIIGetStdout(comm, &vwr)); 8674f7f6c6SJacob Faibussowitsch if (dctx) PetscCall(PetscDeviceContextView(dctx, vwr)); 8774f7f6c6SJacob Faibussowitsch PetscCall(MarkedObjectMapView(vwr, nkeys, keys, modes, ndeps, dependencies)); 8874f7f6c6SJacob Faibussowitsch SETERRQ(comm, PETSC_ERR_PLIB, "Condition '%s' failed, marked object map in corrupt state: %s", cond_str, buf.data()); 8974f7f6c6SJacob Faibussowitsch } 903ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 9174f7f6c6SJacob Faibussowitsch } 9274f7f6c6SJacob 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__); 9374f7f6c6SJacob Faibussowitsch 9474f7f6c6SJacob Faibussowitsch static PetscErrorCode TestAllCombinations(PetscDeviceContext dctx, const std::vector<PetscContainer> &cont) 9574f7f6c6SJacob Faibussowitsch { 9674f7f6c6SJacob Faibussowitsch std::vector<PetscObjectId> cont_ids; 9774f7f6c6SJacob Faibussowitsch PetscObjectId dctx_id; 9874f7f6c6SJacob Faibussowitsch PetscDeviceType dtype; 9974f7f6c6SJacob Faibussowitsch 10074f7f6c6SJacob Faibussowitsch PetscFunctionBegin; 10174f7f6c6SJacob Faibussowitsch PetscCallCXX(cont_ids.reserve(cont.size())); 10274f7f6c6SJacob Faibussowitsch for (auto &&c : cont) { 10374f7f6c6SJacob Faibussowitsch PetscObjectId id; 10474f7f6c6SJacob Faibussowitsch 10574f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectGetId((PetscObject)c, &id)); 10674f7f6c6SJacob Faibussowitsch PetscCallCXX(cont_ids.emplace_back(id)); 10774f7f6c6SJacob Faibussowitsch } 10874f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectGetId(PetscObjectCast(dctx), &dctx_id)); 10974f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype)); 11074f7f6c6SJacob Faibussowitsch for (auto &&func_i : mark_funcs) { 11174f7f6c6SJacob Faibussowitsch for (auto &&func_j : mark_funcs) { 11274f7f6c6SJacob Faibussowitsch for (auto it = cont.cbegin(), next = std::next(it); it != cont.cend(); ++it, ++next) { 11374f7f6c6SJacob Faibussowitsch std::vector<int> found_keys; 11474f7f6c6SJacob Faibussowitsch std::size_t nkeys; 11574f7f6c6SJacob Faibussowitsch PetscObjectId *keys; 11674f7f6c6SJacob Faibussowitsch PetscMemoryAccessMode *modes; 11774f7f6c6SJacob Faibussowitsch std::size_t *ndeps; 11874f7f6c6SJacob Faibussowitsch PetscEvent **dependencies; 11974f7f6c6SJacob Faibussowitsch 12074f7f6c6SJacob Faibussowitsch if (next >= cont.cend()) next = cont.cbegin(); 12174f7f6c6SJacob Faibussowitsch PetscCall(func_i(dctx, *it)); 12274f7f6c6SJacob Faibussowitsch PetscCall(func_j(dctx, *next)); 12374f7f6c6SJacob Faibussowitsch PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies)); 12474f7f6c6SJacob Faibussowitsch PetscCallCXX(found_keys.resize(nkeys)); 12574f7f6c6SJacob Faibussowitsch { 12674f7f6c6SJacob Faibussowitsch // The underlying marked object map is *unordered*, and hence the order in which we 12774f7f6c6SJacob Faibussowitsch // get the keys is not necessarily the same as the order of operations. This is 12874f7f6c6SJacob Faibussowitsch // confounded by the fact that k and knext are not necessarily "linear", i.e. k could 12974f7f6c6SJacob Faibussowitsch // be 2 while knext is 0. So we need to map these back to linear space so we can loop 13074f7f6c6SJacob Faibussowitsch // over them. 13174f7f6c6SJacob Faibussowitsch const auto keys_end = keys + nkeys; 13274f7f6c6SJacob Faibussowitsch const auto num_expected_keys = std::min(cont.size(), static_cast<std::size_t>(2)); 13374f7f6c6SJacob Faibussowitsch const auto check_applied_mode = [&](PetscContainer container, PetscMemoryAccessMode mode) { 13474f7f6c6SJacob Faibussowitsch std::ptrdiff_t key_idx = 0; 13574f7f6c6SJacob Faibussowitsch PetscObjectId actual_key; 13674f7f6c6SJacob Faibussowitsch 13774f7f6c6SJacob Faibussowitsch PetscFunctionBegin; 13874f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectGetId((PetscObject)container, &actual_key)); 13974f7f6c6SJacob Faibussowitsch // search the list of keys from the map for the selected key 14074f7f6c6SJacob Faibussowitsch key_idx = std::distance(keys, std::find(keys, keys_end, actual_key)); 14174f7f6c6SJacob Faibussowitsch PetscCheck(key_idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Key index %" PetscCount_FMT " < 0, this indicates keys_begin > keys_end?", key_idx); 14274f7f6c6SJacob Faibussowitsch found_keys[key_idx]++; 14374f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(key_idx < std::distance(keys, keys_end), "marked object map could not find expected key %" PetscInt64_FMT, actual_key)); 14474f7f6c6SJacob Faibussowitsch // OK found it, now check the rest of the entries are as we expect them to be 14574f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(modes[key_idx] == mode, "unexpected mode %s, expected %s", PetscMemoryAccessModeToString(modes[key_idx]), PetscMemoryAccessModeToString(mode))); 14674f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(ndeps[key_idx] == 1, "unexpected number of dependencies %zu, expected 1", ndeps[key_idx])); 14774f7f6c6SJacob 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])); 1483ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 14974f7f6c6SJacob Faibussowitsch }; 15074f7f6c6SJacob Faibussowitsch 15174f7f6c6SJacob Faibussowitsch // if it == next, then even though we might num_expected_keys keys we never "look 15274f7f6c6SJacob Faibussowitsch // for" the missing key 15313bcc0bdSJacob 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"); 15474f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(nkeys == num_expected_keys, "marked object map has %zu keys expected %zu", nkeys, num_expected_keys)); 15574f7f6c6SJacob Faibussowitsch // check that each function properly applied its mode, it == next if cont.size() = 1, 15674f7f6c6SJacob Faibussowitsch // i.e. testing identity 15774f7f6c6SJacob Faibussowitsch if (it != next) PetscCall(check_applied_mode(*it, func_i.mode)); 15874f7f6c6SJacob Faibussowitsch PetscCall(check_applied_mode(*next, func_j.mode)); 15974f7f6c6SJacob Faibussowitsch } 16074f7f6c6SJacob Faibussowitsch // Check that the map contained only keys we were looking for. Any extra keys will have 16174f7f6c6SJacob Faibussowitsch // zero find count 16274f7f6c6SJacob 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)])); 16374f7f6c6SJacob Faibussowitsch 16474f7f6c6SJacob Faibussowitsch PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies)); 16574f7f6c6SJacob Faibussowitsch 16674f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx)); 16774f7f6c6SJacob Faibussowitsch PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies)); 16874f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(nkeys == 0, "synchronizing device context did not empty dependency map, have %zu keys", nkeys)); 16974f7f6c6SJacob Faibussowitsch PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies)); 17074f7f6c6SJacob Faibussowitsch } 17174f7f6c6SJacob Faibussowitsch } 17274f7f6c6SJacob Faibussowitsch } 17374f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx)); 1743ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 17574f7f6c6SJacob Faibussowitsch } 17674f7f6c6SJacob Faibussowitsch 17774f7f6c6SJacob Faibussowitsch template <typename... T> 17874f7f6c6SJacob Faibussowitsch PETSC_NODISCARD static std::pair<PetscObjectId, std::pair<PetscMemoryAccessMode, std::vector<PetscDeviceContext>>> make_map_entry(PetscObjectId id, PetscMemoryAccessMode mode, T &&...dctxs) 17974f7f6c6SJacob Faibussowitsch { 18074f7f6c6SJacob Faibussowitsch return { 18174f7f6c6SJacob Faibussowitsch id, {mode, {std::forward<T>(dctxs)...}} 18274f7f6c6SJacob Faibussowitsch }; 18374f7f6c6SJacob Faibussowitsch } 18474f7f6c6SJacob Faibussowitsch 18574f7f6c6SJacob Faibussowitsch static PetscErrorCode CheckMapEqual(std::unordered_map<PetscObjectId, std::pair<PetscMemoryAccessMode, std::vector<PetscDeviceContext>>> expected_map) 18674f7f6c6SJacob Faibussowitsch { 18774f7f6c6SJacob Faibussowitsch std::size_t nkeys; 18874f7f6c6SJacob Faibussowitsch PetscObjectId *keys; 18974f7f6c6SJacob Faibussowitsch PetscMemoryAccessMode *modes; 19074f7f6c6SJacob Faibussowitsch std::size_t *ndeps; 19174f7f6c6SJacob Faibussowitsch PetscEvent **dependencies; 19274f7f6c6SJacob Faibussowitsch PetscDeviceContext dctx = nullptr; 19374f7f6c6SJacob Faibussowitsch 19474f7f6c6SJacob Faibussowitsch PetscFunctionBegin; 19574f7f6c6SJacob Faibussowitsch PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies)); 19674f7f6c6SJacob Faibussowitsch { 19774f7f6c6SJacob Faibussowitsch const auto key_end = keys + nkeys; 19874f7f6c6SJacob Faibussowitsch auto mode_it = modes; 19974f7f6c6SJacob Faibussowitsch auto ndep_it = ndeps; 20074f7f6c6SJacob Faibussowitsch auto dep_it = dependencies; 20174f7f6c6SJacob Faibussowitsch 20274f7f6c6SJacob Faibussowitsch for (auto key_it = keys; key_it != key_end; ++key_it, ++mode_it, ++ndep_it, ++dep_it) { 20374f7f6c6SJacob Faibussowitsch const auto found_it = expected_map.find(*key_it); 20474f7f6c6SJacob Faibussowitsch 20574f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(found_it != expected_map.cend(), "marked object map did not contain key %" PetscInt64_FMT, *key_it)); 20674f7f6c6SJacob Faibussowitsch { 20774f7f6c6SJacob Faibussowitsch // must do these here since found_it may be expected_map.cend() 20874f7f6c6SJacob Faibussowitsch const auto &expected_mode = found_it->second.first; 20974f7f6c6SJacob Faibussowitsch const auto &expected_dctxs = found_it->second.second; 21074f7f6c6SJacob Faibussowitsch auto sub_dep_it = *dep_it; 21174f7f6c6SJacob Faibussowitsch 21274f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(expected_mode == *mode_it, "unexpected mode %s, expected %s", PetscMemoryAccessModeToString(expected_mode), PetscMemoryAccessModeToString(*mode_it))); 21374f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(expected_dctxs.size() == *ndep_it, "unexpected number of dependencies %zu, expected %zu", *ndep_it, expected_dctxs.size())); 21474f7f6c6SJacob Faibussowitsch // purposefully hide "dctx" with the loop variable, so we get more detailed output in 21574f7f6c6SJacob Faibussowitsch // the error message 21674f7f6c6SJacob Faibussowitsch for (auto &&dctx : expected_dctxs) { 21774f7f6c6SJacob Faibussowitsch const auto event = *sub_dep_it; 21874f7f6c6SJacob Faibussowitsch PetscDeviceType dtype; 21974f7f6c6SJacob Faibussowitsch PetscObjectId id; 22074f7f6c6SJacob Faibussowitsch 22174f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype)); 22274f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectGetId(PetscObjectCast(dctx), &id)); 22374f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(event->dtype == dtype, "unexpected device type on event: %s, expected %s", PetscDeviceTypes[event->dtype], PetscDeviceTypes[dtype])); 22474f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(event->dctx_id == id, "unexpected dctx id on event: %" PetscInt64_FMT ", expected %" PetscInt64_FMT, event->dctx_id, id)); 22574f7f6c6SJacob Faibussowitsch ++sub_dep_it; 22674f7f6c6SJacob Faibussowitsch } 22774f7f6c6SJacob Faibussowitsch } 22874f7f6c6SJacob Faibussowitsch // remove the found iterator from the map, this ensure we either run out of map (which is 229aaa8cc7dSPierre Jolivet // caught by the first check in the loop), or we run out of keys to check, which is 23074f7f6c6SJacob Faibussowitsch // caught in the end of the loop 23174f7f6c6SJacob Faibussowitsch PetscCallCXX(expected_map.erase(found_it)); 23274f7f6c6SJacob Faibussowitsch } 23374f7f6c6SJacob Faibussowitsch } 23474f7f6c6SJacob Faibussowitsch PetscCall(CheckMarkedObjectMap(expected_map.empty(), "Not all keys in marked object map accounted for!")); 23574f7f6c6SJacob Faibussowitsch PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies)); 2363ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 23774f7f6c6SJacob Faibussowitsch } 23874f7f6c6SJacob Faibussowitsch 23974f7f6c6SJacob Faibussowitsch int main(int argc, char *argv[]) 24074f7f6c6SJacob Faibussowitsch { 24174f7f6c6SJacob Faibussowitsch PetscContainer x, y, z; 24274f7f6c6SJacob Faibussowitsch PetscObjectId x_id, y_id, z_id; 24374f7f6c6SJacob Faibussowitsch PetscDeviceContext dctx_a, dctx_b, dctx_c; 24474f7f6c6SJacob Faibussowitsch auto container_view = PETSC_FALSE; 24574f7f6c6SJacob Faibussowitsch const auto create_container = [&](PetscContainer *c, const char name[], PetscObjectId *id) { 24674f7f6c6SJacob Faibussowitsch PetscFunctionBegin; 24774f7f6c6SJacob Faibussowitsch PetscCall(PetscContainerCreate(PETSC_COMM_WORLD, c)); 24874f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectSetName((PetscObject)(*c), name)); 24974f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectGetId((PetscObject)(*c), id)); 25074f7f6c6SJacob Faibussowitsch if (container_view) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Container '%s' -> id %" PetscInt64_FMT "\n", name, *id)); 2513ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 25274f7f6c6SJacob Faibussowitsch }; 25374f7f6c6SJacob Faibussowitsch const auto sync_all = [&] { 25474f7f6c6SJacob Faibussowitsch PetscFunctionBegin; 25574f7f6c6SJacob Faibussowitsch for (auto &&ctx : {dctx_a, dctx_b, dctx_c}) PetscCall(PetscDeviceContextSynchronize(ctx)); 2563ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 25774f7f6c6SJacob Faibussowitsch }; 25874f7f6c6SJacob Faibussowitsch 25974f7f6c6SJacob Faibussowitsch PetscFunctionBeginUser; 26074f7f6c6SJacob Faibussowitsch PetscCall(PetscInitialize(&argc, &argv, nullptr, help)); 26174f7f6c6SJacob Faibussowitsch 26274f7f6c6SJacob Faibussowitsch PetscOptionsBegin(PETSC_COMM_WORLD, nullptr, "Test Options", "Sys"); 26374f7f6c6SJacob Faibussowitsch PetscCall(PetscOptionsBool("-container_view", "View container names and ID's", nullptr, container_view, &container_view, nullptr)); 26474f7f6c6SJacob Faibussowitsch PetscOptionsEnd(); 26574f7f6c6SJacob Faibussowitsch 26674f7f6c6SJacob Faibussowitsch PetscCall(create_container(&x, "x", &x_id)); 26774f7f6c6SJacob Faibussowitsch PetscCall(create_container(&y, "y", &y_id)); 26874f7f6c6SJacob Faibussowitsch PetscCall(create_container(&z, "z", &z_id)); 26974f7f6c6SJacob Faibussowitsch 27074f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextCreate(&dctx_a)); 27174f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectSetName(PetscObjectCast(dctx_a), "dctx_a")); 272*d9acb416SHong Zhang PetscCall(PetscDeviceContextSetStreamType(dctx_a, PETSC_STREAM_DEFAULT)); 27374f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSetFromOptions(PETSC_COMM_WORLD, dctx_a)); 27474f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextDuplicate(dctx_a, &dctx_b)); 27574f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectSetName(PetscObjectCast(dctx_b), "dctx_b")); 27674f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextDuplicate(dctx_a, &dctx_c)); 27774f7f6c6SJacob Faibussowitsch PetscCall(PetscObjectSetName(PetscObjectCast(dctx_c), "dctx_c")); 27874f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextViewFromOptions(dctx_a, nullptr, "-dctx_a_view")); 27974f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextViewFromOptions(dctx_b, nullptr, "-dctx_b_view")); 28074f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextViewFromOptions(dctx_c, nullptr, "-dctx_c_view")); 28174f7f6c6SJacob Faibussowitsch 28274f7f6c6SJacob Faibussowitsch // ensure they are all idle 28374f7f6c6SJacob Faibussowitsch PetscCall(sync_all()); 28474f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 28574f7f6c6SJacob Faibussowitsch 28674f7f6c6SJacob Faibussowitsch // do the bulk combination tests, these test only the very basic combinations for simple 28774f7f6c6SJacob Faibussowitsch // correctness 28874f7f6c6SJacob Faibussowitsch PetscCall(TestAllCombinations(dctx_a, {x})); 28974f7f6c6SJacob Faibussowitsch PetscCall(TestAllCombinations(dctx_a, {x, y, z})); 29074f7f6c6SJacob Faibussowitsch 29174f7f6c6SJacob Faibussowitsch // Now do some specific tests, these should test more complicated scenarios. First and 29274f7f6c6SJacob Faibussowitsch // foremost, ensure they are all idle, and that it does not change the map 29374f7f6c6SJacob Faibussowitsch PetscCall(sync_all()); 29474f7f6c6SJacob Faibussowitsch // Map should be empty 29574f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 29674f7f6c6SJacob Faibussowitsch 29774f7f6c6SJacob Faibussowitsch // Syncing again shouldn't magically fill the map back up 29874f7f6c6SJacob Faibussowitsch PetscCall(sync_all()); 29974f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 30074f7f6c6SJacob Faibussowitsch 30174f7f6c6SJacob Faibussowitsch const auto test_multiple_readers = [&](std::array<PetscDeviceContext, 2> readers, std::size_t sync_idx) { 30274f7f6c6SJacob Faibussowitsch // the reader which synchronizes 30374f7f6c6SJacob Faibussowitsch const auto sync_reader = readers[sync_idx]; 30474f7f6c6SJacob Faibussowitsch // the reader that will remain in the map after sync_reader synchronizes 30574f7f6c6SJacob Faibussowitsch const auto remain_idx = sync_idx + 1 >= readers.size() ? 0 : sync_idx + 1; 30674f7f6c6SJacob Faibussowitsch const auto remain_reader = readers[remain_idx]; 30774f7f6c6SJacob Faibussowitsch 30874f7f6c6SJacob Faibussowitsch PetscFunctionBegin; 30989e5c462SJacob Faibussowitsch for (auto &&ctx : readers) PetscCall(mem_read(ctx, x)); 31089e5c462SJacob Faibussowitsch for (auto &&ctx : readers) PetscCall(mem_read(ctx, y)); 31174f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({ 31274f7f6c6SJacob Faibussowitsch make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, readers[0], readers[1]), 31374f7f6c6SJacob Faibussowitsch make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, readers[0], readers[1]), 31474f7f6c6SJacob Faibussowitsch })); 31574f7f6c6SJacob Faibussowitsch // synchronizing sync_reader should remove it from the dependency list -- but leave remain_reader 31674f7f6c6SJacob Faibussowitsch // intact 31774f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(sync_reader)); 31874f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({ 31974f7f6c6SJacob Faibussowitsch make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, remain_reader), 32074f7f6c6SJacob Faibussowitsch make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, remain_reader), 32174f7f6c6SJacob Faibussowitsch })); 32274f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(remain_reader)); 32374f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 3243ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 32574f7f6c6SJacob Faibussowitsch }; 32674f7f6c6SJacob Faibussowitsch 327aaa8cc7dSPierre Jolivet // Test that multiple readers can simultaneously read -- even if one of them is synchronized 32874f7f6c6SJacob Faibussowitsch PetscCall(test_multiple_readers({dctx_a, dctx_b}, 0)); 32974f7f6c6SJacob Faibussowitsch PetscCall(test_multiple_readers({dctx_a, dctx_b}, 1)); 33074f7f6c6SJacob Faibussowitsch 33174f7f6c6SJacob Faibussowitsch // Test that sync of unrelated ctx does not affect the map 33289e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_a, x)); 33389e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_b, y)); 33474f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_c)); 33574f7f6c6SJacob Faibussowitsch // clang-format off 33674f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({ 33774f7f6c6SJacob Faibussowitsch make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a), 33874f7f6c6SJacob Faibussowitsch make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, dctx_b) 33974f7f6c6SJacob Faibussowitsch })); 34074f7f6c6SJacob Faibussowitsch // clang-format on 34174f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_a)); 34274f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_b)); 34374f7f6c6SJacob Faibussowitsch // Now the map is empty again 34474f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 34574f7f6c6SJacob Faibussowitsch 34674f7f6c6SJacob Faibussowitsch // Test another context writing over two reads 34789e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_a, x)); 34889e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_b, x)); 34974f7f6c6SJacob Faibussowitsch // C writing should kick out both A and B 35089e5c462SJacob Faibussowitsch PetscCall(mem_write(dctx_c, x)); 35174f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_c)})); 35274f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_c)); 35374f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 35474f7f6c6SJacob Faibussowitsch 35574f7f6c6SJacob Faibussowitsch // Test that write and synchronize does not interfere with unrelated read 35689e5c462SJacob Faibussowitsch PetscCall(mem_read_write(dctx_a, x)); 35789e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_a, y)); 35889e5c462SJacob Faibussowitsch PetscCall(mem_read_write(dctx_b, x)); 35989e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_b, y)); 36074f7f6c6SJacob Faibussowitsch // Synchronizing B here must clear everything *but* A's read on Y! 36174f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_b)); 36274f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, dctx_a)})); 36374f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_a)); 36474f7f6c6SJacob Faibussowitsch // Now the map is empty again 36574f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 36674f7f6c6SJacob Faibussowitsch 36774f7f6c6SJacob Faibussowitsch // Test that implicit stream-dependencies are properly tracked 36889e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_a, x)); 36989e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_b, y)); 37074f7f6c6SJacob Faibussowitsch // A waits for B 37174f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextWaitForContext(dctx_a, dctx_b)); 37274f7f6c6SJacob Faibussowitsch // Because A waits on B, synchronizing A implicitly implies B read must have finished so the 37374f7f6c6SJacob Faibussowitsch // map must be empty 37474f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_a)); 37574f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 37674f7f6c6SJacob Faibussowitsch 37789e5c462SJacob Faibussowitsch PetscCall(mem_write(dctx_a, x)); 37874f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_a)})); 37974f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextWaitForContext(dctx_b, dctx_a)); 38074f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextWaitForContext(dctx_c, dctx_b)); 38174f7f6c6SJacob Faibussowitsch // We have created the chain C -> B -> A, so synchronizing C should trickle down to synchronize and 38274f7f6c6SJacob Faibussowitsch // remove A from the map 38374f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_c)); 38474f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 38574f7f6c6SJacob Faibussowitsch 38674f7f6c6SJacob Faibussowitsch // Test that superfluous stream-dependencies are properly ignored 38789e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_a, x)); 38889e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_b, y)); 38974f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextWaitForContext(dctx_c, dctx_b)); 39074f7f6c6SJacob Faibussowitsch // C waited on B, so synchronizing C should remove B from the map but *not* remove A 39174f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_c)); 39274f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a)})); 39374f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_a)); 39474f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 39574f7f6c6SJacob Faibussowitsch 39674f7f6c6SJacob Faibussowitsch // Test that read->write correctly wipes out the map 39789e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_a, x)); 39889e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_b, x)); 39989e5c462SJacob Faibussowitsch PetscCall(mem_read(dctx_c, x)); 40074f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a, dctx_b, dctx_c)})); 40189e5c462SJacob Faibussowitsch PetscCall(mem_write(dctx_a, x)); 40274f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_a)})); 40374f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextSynchronize(dctx_a)); 40474f7f6c6SJacob Faibussowitsch PetscCall(CheckMapEqual({})); 40574f7f6c6SJacob Faibussowitsch 40674f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextDestroy(&dctx_a)); 40774f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextDestroy(&dctx_b)); 40874f7f6c6SJacob Faibussowitsch PetscCall(PetscDeviceContextDestroy(&dctx_c)); 40974f7f6c6SJacob Faibussowitsch 41074f7f6c6SJacob Faibussowitsch PetscCall(PetscContainerDestroy(&x)); 41174f7f6c6SJacob Faibussowitsch PetscCall(PetscContainerDestroy(&y)); 41274f7f6c6SJacob Faibussowitsch PetscCall(PetscContainerDestroy(&z)); 41374f7f6c6SJacob Faibussowitsch PetscCall(PetscPrintf(PETSC_COMM_WORLD, "EXIT_SUCCESS\n")); 41474f7f6c6SJacob Faibussowitsch PetscCall(PetscFinalize()); 41574f7f6c6SJacob Faibussowitsch return 0; 41674f7f6c6SJacob Faibussowitsch } 41774f7f6c6SJacob Faibussowitsch 41874f7f6c6SJacob Faibussowitsch /*TEST 41974f7f6c6SJacob Faibussowitsch 42074f7f6c6SJacob Faibussowitsch testset: 42174f7f6c6SJacob Faibussowitsch requires: cxx 42274f7f6c6SJacob Faibussowitsch output_file: ./output/ExitSuccess.out 42374f7f6c6SJacob Faibussowitsch test: 42474f7f6c6SJacob Faibussowitsch requires: !device 42574f7f6c6SJacob Faibussowitsch suffix: host_no_device 42674f7f6c6SJacob Faibussowitsch test: 42774f7f6c6SJacob Faibussowitsch requires: device 42874f7f6c6SJacob Faibussowitsch args: -default_device_type host 42974f7f6c6SJacob Faibussowitsch suffix: host_with_device 43074f7f6c6SJacob Faibussowitsch test: 43174f7f6c6SJacob Faibussowitsch requires: cuda 43274f7f6c6SJacob Faibussowitsch args: -default_device_type cuda 43374f7f6c6SJacob Faibussowitsch suffix: cuda 43474f7f6c6SJacob Faibussowitsch test: 43574f7f6c6SJacob Faibussowitsch requires: hip 43674f7f6c6SJacob Faibussowitsch args: -default_device_type hip 43774f7f6c6SJacob Faibussowitsch suffix: hip 43874f7f6c6SJacob Faibussowitsch test: 43974f7f6c6SJacob Faibussowitsch requires: sycl 44074f7f6c6SJacob Faibussowitsch args: -default_device_type sycl 44174f7f6c6SJacob Faibussowitsch suffix: sycl 44274f7f6c6SJacob Faibussowitsch 44374f7f6c6SJacob Faibussowitsch TEST*/ 444