Actual source code: ex11.cxx
1: static const char help[] = "Tests PetscDeviceContextMarkIntentFromID().\n\n";
3: #include "petscdevicetestcommon.h"
4: #include <petscviewer.h>
6: #include <petsc/private/cpp/type_traits.hpp>
7: #include <petsc/private/cpp/array.hpp>
9: #include <cstdarg> // std::va_list
10: #include <vector> // std:vector
11: #include <unordered_map> // std::take_a_wild_guess
12: #include <algorithm> // std::find
13: #include <iterator> // std::distance, std::next
15: #include <petscmacros.h>
17: #if PETSC_CPP_VERSION > 14
18: struct Marker {
19: PetscMemoryAccessMode mode{};
21: PetscErrorCode operator()(PetscDeviceContext dctx, PetscContainer cont) const noexcept
22: {
23: const auto obj = reinterpret_cast<PetscObject>(cont);
24: PetscObjectId id = 0;
25: const char *name = nullptr;
27: PetscFunctionBegin;
28: PetscCall(PetscObjectGetId(obj, &id));
29: PetscCall(PetscObjectGetName(obj, &name));
30: PetscCall(PetscDeviceContextMarkIntentFromID(dctx, id, this->mode, name));
31: PetscFunctionReturn(PETSC_SUCCESS);
32: }
33: };
35: static constexpr auto mem_read = Marker{PETSC_MEMORY_ACCESS_READ};
36: static constexpr auto mem_write = Marker{PETSC_MEMORY_ACCESS_WRITE};
37: static constexpr auto mem_read_write = Marker{PETSC_MEMORY_ACCESS_READ_WRITE};
38: static constexpr auto mark_funcs = Petsc::util::make_array(mem_read, mem_write, mem_read_write);
40: static PetscErrorCode MarkedObjectMapView(PetscViewer vwr, std::size_t nkeys, const PetscObjectId *keys, const PetscMemoryAccessMode *modes, const std::size_t *ndeps, const PetscEvent **dependencies)
41: {
42: PetscFunctionBegin;
43: if (!vwr) PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &vwr));
44: PetscCall(PetscViewerFlush(vwr));
45: PetscCall(PetscViewerASCIIPushSynchronized(vwr));
46: PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "Marked Object Map:\n"));
47: PetscCall(PetscViewerASCIIPushTab(vwr));
48: PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "size: %zu\n", nkeys));
49: PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "entries:\n"));
50: PetscCall(PetscViewerASCIIPushTab(vwr));
51: for (std::size_t i = 0; i < nkeys; ++i) {
52: PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "id %" PetscInt64_FMT " -> {\n", keys[i]));
53: PetscCall(PetscViewerASCIIPushTab(vwr));
54: PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "mode: %s\n", PetscMemoryAccessModeToString(modes[i])));
55: PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "dependencies:\n"));
56: PetscCall(PetscViewerASCIIPushTab(vwr));
57: for (std::size_t j = 0; j < ndeps[i]; ++j) {
58: const auto event = dependencies[i][j];
60: 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,
61: reinterpret_cast<void *>(event->destroy)));
62: }
63: PetscCall(PetscViewerASCIIPopTab(vwr));
64: PetscCall(PetscViewerASCIIPopTab(vwr));
65: PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "}\n"));
66: }
67: PetscCall(PetscViewerASCIIPopTab(vwr));
68: PetscCall(PetscViewerASCIIPopTab(vwr));
69: PetscCall(PetscViewerFlush(vwr));
70: PetscCall(PetscViewerASCIIPopSynchronized(vwr));
71: PetscFunctionReturn(PETSC_SUCCESS);
72: }
74: 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, ...)
75: {
76: PetscFunctionBegin;
77: if (PetscUnlikely(!cond)) {
78: std::array<char, 2048> buf;
79: std::va_list argp;
80: std::size_t len;
81: PetscViewer vwr;
83: PetscCallCXX(buf.fill(0));
84: va_start(argp, format);
85: PetscCall(PetscVSNPrintf(buf.data(), buf.size(), format, &len, argp));
86: va_end(argp);
87: PetscCall(PetscViewerASCIIGetStdout(comm, &vwr));
88: if (dctx) PetscCall(PetscDeviceContextView(dctx, vwr));
89: PetscCall(MarkedObjectMapView(vwr, nkeys, keys, modes, ndeps, dependencies));
90: SETERRQ(comm, PETSC_ERR_PLIB, "Condition '%s' failed, marked object map in corrupt state: %s", cond_str, buf.data());
91: }
92: PetscFunctionReturn(PETSC_SUCCESS);
93: }
95: #define CheckMarkedObjectMap(__cond__, ...) CheckMarkedObjectMap_Private((PetscBool)(!!(__cond__)), PetscStringize(__cond__), PETSC_COMM_SELF, dctx, nkeys, keys, modes, ndeps, const_cast<const PetscEvent **>(dependencies), __VA_ARGS__);
97: static PetscErrorCode TestAllCombinations(PetscDeviceContext dctx, const std::vector<PetscContainer> &cont)
98: {
99: std::vector<PetscObjectId> cont_ids;
100: PetscObjectId dctx_id;
101: PetscDeviceType dtype;
103: PetscFunctionBegin;
104: PetscCallCXX(cont_ids.reserve(cont.size()));
105: for (auto &&c : cont) {
106: PetscObjectId id;
108: PetscCall(PetscObjectGetId((PetscObject)c, &id));
109: PetscCallCXX(cont_ids.emplace_back(id));
110: }
111: PetscCall(PetscObjectGetId(PetscObjectCast(dctx), &dctx_id));
112: PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype));
113: for (auto &&func_i : mark_funcs) {
114: for (auto &&func_j : mark_funcs) {
115: for (auto it = cont.cbegin(), next = std::next(it); it != cont.cend(); ++it, ++next) {
116: std::vector<int> found_keys;
117: std::size_t nkeys;
118: PetscObjectId *keys;
119: PetscMemoryAccessMode *modes;
120: std::size_t *ndeps;
121: PetscEvent **dependencies;
123: if (next >= cont.cend()) next = cont.cbegin();
124: PetscCall(func_i(dctx, *it));
125: PetscCall(func_j(dctx, *next));
126: PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies));
127: PetscCallCXX(found_keys.resize(nkeys));
128: {
129: // The underlying marked object map is *unordered*, and hence the order in which we
130: // get the keys is not necessarily the same as the order of operations. This is
131: // confounded by the fact that k and knext are not necessarily "linear", i.e. k could
132: // be 2 while knext is 0. So we need to map these back to linear space so we can loop
133: // over them.
134: const auto keys_end = keys + nkeys;
135: const auto num_expected_keys = std::min(cont.size(), static_cast<std::size_t>(2));
136: const auto check_applied_mode = [&](PetscContainer container, PetscMemoryAccessMode mode) {
137: std::ptrdiff_t key_idx = 0;
138: PetscObjectId actual_key;
140: PetscFunctionBegin;
141: PetscCall(PetscObjectGetId((PetscObject)container, &actual_key));
142: // search the list of keys from the map for the selected key
143: key_idx = std::distance(keys, std::find(keys, keys_end, actual_key));
144: PetscCheck(key_idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Key index %" PetscCount_FMT " < 0, this indicates keys_begin > keys_end?", key_idx);
145: found_keys[key_idx]++;
146: PetscCall(CheckMarkedObjectMap(key_idx < std::distance(keys, keys_end), "marked object map could not find expected key %" PetscInt64_FMT, actual_key));
147: // OK found it, now check the rest of the entries are as we expect them to be
148: PetscCall(CheckMarkedObjectMap(modes[key_idx] == mode, "unexpected mode %s, expected %s", PetscMemoryAccessModeToString(modes[key_idx]), PetscMemoryAccessModeToString(mode)));
149: PetscCall(CheckMarkedObjectMap(ndeps[key_idx] == 1, "unexpected number of dependencies %zu, expected 1", ndeps[key_idx]));
150: PetscCall(CheckMarkedObjectMap(dependencies[key_idx][0]->dtype == dtype, "unexpected device type on event: %s, expected %s", PetscDeviceTypes[dependencies[key_idx][0]->dtype], PetscDeviceTypes[dtype]));
151: PetscFunctionReturn(PETSC_SUCCESS);
152: };
154: // if it == next, then even though we might num_expected_keys keys we never "look
155: // for" the missing key
156: 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");
157: PetscCall(CheckMarkedObjectMap(nkeys == num_expected_keys, "marked object map has %zu keys expected %zu", nkeys, num_expected_keys));
158: // check that each function properly applied its mode, it == next if cont.size() = 1,
159: // i.e. testing identity
160: if (it != next) PetscCall(check_applied_mode(*it, func_i.mode));
161: PetscCall(check_applied_mode(*next, func_j.mode));
162: }
163: // Check that the map contained only keys we were looking for. Any extra keys will have
164: // zero find count
165: 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)]));
167: PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies));
169: PetscCall(PetscDeviceContextSynchronize(dctx));
170: PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies));
171: PetscCall(CheckMarkedObjectMap(nkeys == 0, "synchronizing device context did not empty dependency map, have %zu keys", nkeys));
172: PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies));
173: }
174: }
175: }
176: PetscCall(PetscDeviceContextSynchronize(dctx));
177: PetscFunctionReturn(PETSC_SUCCESS);
178: }
180: template <typename... T>
181: PETSC_NODISCARD static std::pair<PetscObjectId, std::pair<PetscMemoryAccessMode, std::vector<PetscDeviceContext>>> make_map_entry(PetscObjectId id, PetscMemoryAccessMode mode, T &&...dctxs)
182: {
183: return {
184: id, {mode, {std::forward<T>(dctxs)...}}
185: };
186: }
188: static PetscErrorCode CheckMapEqual(std::unordered_map<PetscObjectId, std::pair<PetscMemoryAccessMode, std::vector<PetscDeviceContext>>> expected_map)
189: {
190: std::size_t nkeys;
191: PetscObjectId *keys;
192: PetscMemoryAccessMode *modes;
193: std::size_t *ndeps;
194: PetscEvent **dependencies;
195: PetscDeviceContext dctx = nullptr;
197: PetscFunctionBegin;
198: PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies));
199: {
200: const auto key_end = keys + nkeys;
201: auto mode_it = modes;
202: auto ndep_it = ndeps;
203: auto dep_it = dependencies;
205: for (auto key_it = keys; key_it != key_end; ++key_it, ++mode_it, ++ndep_it, ++dep_it) {
206: const auto found_it = expected_map.find(*key_it);
208: PetscCall(CheckMarkedObjectMap(found_it != expected_map.cend(), "marked object map did not contain key %" PetscInt64_FMT, *key_it));
209: {
210: // must do these here since found_it may be expected_map.cend()
211: const auto &expected_mode = found_it->second.first;
212: const auto &expected_dctxs = found_it->second.second;
213: auto sub_dep_it = *dep_it;
215: PetscCall(CheckMarkedObjectMap(expected_mode == *mode_it, "unexpected mode %s, expected %s", PetscMemoryAccessModeToString(expected_mode), PetscMemoryAccessModeToString(*mode_it)));
216: PetscCall(CheckMarkedObjectMap(expected_dctxs.size() == *ndep_it, "unexpected number of dependencies %zu, expected %zu", *ndep_it, expected_dctxs.size()));
217: // purposefully hide "dctx" with the loop variable, so we get more detailed output in
218: // the error message
219: for (auto &&dctx : expected_dctxs) {
220: const auto event = *sub_dep_it;
221: PetscDeviceType dtype;
222: PetscObjectId id;
224: PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype));
225: PetscCall(PetscObjectGetId(PetscObjectCast(dctx), &id));
226: PetscCall(CheckMarkedObjectMap(event->dtype == dtype, "unexpected device type on event: %s, expected %s", PetscDeviceTypes[event->dtype], PetscDeviceTypes[dtype]));
227: PetscCall(CheckMarkedObjectMap(event->dctx_id == id, "unexpected dctx id on event: %" PetscInt64_FMT ", expected %" PetscInt64_FMT, event->dctx_id, id));
228: ++sub_dep_it;
229: }
230: }
231: // remove the found iterator from the map, this ensure we either run out of map (which is
232: // caught by the first check in the loop), or we run out of keys to check, which is
233: // caught in the end of the loop
234: PetscCallCXX(expected_map.erase(found_it));
235: }
236: }
237: PetscCall(CheckMarkedObjectMap(expected_map.empty(), "Not all keys in marked object map accounted for!"));
238: PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies));
239: PetscFunctionReturn(PETSC_SUCCESS);
240: }
242: int main(int argc, char *argv[])
243: {
244: PetscContainer x, y, z;
245: PetscObjectId x_id, y_id, z_id;
246: PetscDeviceContext dctx_a, dctx_b, dctx_c;
247: auto container_view = PETSC_FALSE;
248: const auto create_container = [&](PetscContainer *c, const char name[], PetscObjectId *id) {
249: PetscFunctionBegin;
250: PetscCall(PetscContainerCreate(PETSC_COMM_WORLD, c));
251: PetscCall(PetscObjectSetName((PetscObject)*c, name));
252: PetscCall(PetscObjectGetId((PetscObject)*c, id));
253: if (container_view) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Container '%s' -> id %" PetscInt64_FMT "\n", name, *id));
254: PetscFunctionReturn(PETSC_SUCCESS);
255: };
256: const auto sync_all = [&] {
257: PetscFunctionBegin;
258: for (auto &&ctx : {dctx_a, dctx_b, dctx_c}) PetscCall(PetscDeviceContextSynchronize(ctx));
259: PetscFunctionReturn(PETSC_SUCCESS);
260: };
262: PetscFunctionBeginUser;
263: PetscCall(PetscInitialize(&argc, &argv, nullptr, help));
265: PetscOptionsBegin(PETSC_COMM_WORLD, nullptr, "Test Options", "Sys");
266: PetscCall(PetscOptionsBool("-container_view", "View container names and ID's", nullptr, container_view, &container_view, nullptr));
267: PetscOptionsEnd();
269: PetscCall(create_container(&x, "x", &x_id));
270: PetscCall(create_container(&y, "y", &y_id));
271: PetscCall(create_container(&z, "z", &z_id));
273: PetscCall(PetscDeviceContextCreate(&dctx_a));
274: PetscCall(PetscObjectSetName(PetscObjectCast(dctx_a), "dctx_a"));
275: PetscCall(PetscDeviceContextSetStreamType(dctx_a, PETSC_STREAM_DEFAULT));
276: PetscCall(PetscDeviceContextSetFromOptions(PETSC_COMM_WORLD, dctx_a));
277: PetscCall(PetscDeviceContextDuplicate(dctx_a, &dctx_b));
278: PetscCall(PetscObjectSetName(PetscObjectCast(dctx_b), "dctx_b"));
279: PetscCall(PetscDeviceContextDuplicate(dctx_a, &dctx_c));
280: PetscCall(PetscObjectSetName(PetscObjectCast(dctx_c), "dctx_c"));
281: PetscCall(PetscDeviceContextViewFromOptions(dctx_a, nullptr, "-dctx_a_view"));
282: PetscCall(PetscDeviceContextViewFromOptions(dctx_b, nullptr, "-dctx_b_view"));
283: PetscCall(PetscDeviceContextViewFromOptions(dctx_c, nullptr, "-dctx_c_view"));
285: // ensure they are all idle
286: PetscCall(sync_all());
287: PetscCall(CheckMapEqual({}));
289: // do the bulk combination tests, these test only the very basic combinations for simple
290: // correctness
291: PetscCall(TestAllCombinations(dctx_a, {x}));
292: PetscCall(TestAllCombinations(dctx_a, {x, y, z}));
294: // Now do some specific tests, these should test more complicated scenarios. First and
295: // foremost, ensure they are all idle, and that it does not change the map
296: PetscCall(sync_all());
297: // Map should be empty
298: PetscCall(CheckMapEqual({}));
300: // Syncing again shouldn't magically fill the map back up
301: PetscCall(sync_all());
302: PetscCall(CheckMapEqual({}));
304: const auto test_multiple_readers = [&](std::array<PetscDeviceContext, 2> readers, std::size_t sync_idx) {
305: // the reader which synchronizes
306: const auto sync_reader = readers[sync_idx];
307: // the reader that will remain in the map after sync_reader synchronizes
308: const auto remain_idx = sync_idx + 1 >= readers.size() ? 0 : sync_idx + 1;
309: const auto remain_reader = readers[remain_idx];
311: PetscFunctionBegin;
312: for (auto &&ctx : readers) PetscCall(mem_read(ctx, x));
313: for (auto &&ctx : readers) PetscCall(mem_read(ctx, y));
314: PetscCall(CheckMapEqual({
315: make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, readers[0], readers[1]),
316: make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, readers[0], readers[1]),
317: }));
318: // synchronizing sync_reader should remove it from the dependency list -- but leave remain_reader
319: // intact
320: PetscCall(PetscDeviceContextSynchronize(sync_reader));
321: PetscCall(CheckMapEqual({
322: make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, remain_reader),
323: make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, remain_reader),
324: }));
325: PetscCall(PetscDeviceContextSynchronize(remain_reader));
326: PetscCall(CheckMapEqual({}));
327: PetscFunctionReturn(PETSC_SUCCESS);
328: };
330: // Test that multiple readers can simultaneously read -- even if one of them is synchronized
331: PetscCall(test_multiple_readers({dctx_a, dctx_b}, 0));
332: PetscCall(test_multiple_readers({dctx_a, dctx_b}, 1));
334: // Test that sync of unrelated ctx does not affect the map
335: PetscCall(mem_read(dctx_a, x));
336: PetscCall(mem_read(dctx_b, y));
337: PetscCall(PetscDeviceContextSynchronize(dctx_c));
338: // clang-format off
339: PetscCall(CheckMapEqual({
340: make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a),
341: make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, dctx_b)
342: }));
343: // clang-format on
344: PetscCall(PetscDeviceContextSynchronize(dctx_a));
345: PetscCall(PetscDeviceContextSynchronize(dctx_b));
346: // Now the map is empty again
347: PetscCall(CheckMapEqual({}));
349: // Test another context writing over two reads
350: PetscCall(mem_read(dctx_a, x));
351: PetscCall(mem_read(dctx_b, x));
352: // C writing should kick out both A and B
353: PetscCall(mem_write(dctx_c, x));
354: PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_c)}));
355: PetscCall(PetscDeviceContextSynchronize(dctx_c));
356: PetscCall(CheckMapEqual({}));
358: // Test that write and synchronize does not interfere with unrelated read
359: PetscCall(mem_read_write(dctx_a, x));
360: PetscCall(mem_read(dctx_a, y));
361: PetscCall(mem_read_write(dctx_b, x));
362: PetscCall(mem_read(dctx_b, y));
363: // Synchronizing B here must clear everything *but* A's read on Y!
364: PetscCall(PetscDeviceContextSynchronize(dctx_b));
365: PetscCall(CheckMapEqual({make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, dctx_a)}));
366: PetscCall(PetscDeviceContextSynchronize(dctx_a));
367: // Now the map is empty again
368: PetscCall(CheckMapEqual({}));
370: // Test that implicit stream-dependencies are properly tracked
371: PetscCall(mem_read(dctx_a, x));
372: PetscCall(mem_read(dctx_b, y));
373: // A waits for B
374: PetscCall(PetscDeviceContextWaitForContext(dctx_a, dctx_b));
375: // Because A waits on B, synchronizing A implicitly implies B read must have finished so the
376: // map must be empty
377: PetscCall(PetscDeviceContextSynchronize(dctx_a));
378: PetscCall(CheckMapEqual({}));
380: PetscCall(mem_write(dctx_a, x));
381: PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_a)}));
382: PetscCall(PetscDeviceContextWaitForContext(dctx_b, dctx_a));
383: PetscCall(PetscDeviceContextWaitForContext(dctx_c, dctx_b));
384: // We have created the chain C -> B -> A, so synchronizing C should trickle down to synchronize and
385: // remove A from the map
386: PetscCall(PetscDeviceContextSynchronize(dctx_c));
387: PetscCall(CheckMapEqual({}));
389: // Test that superfluous stream-dependencies are properly ignored
390: PetscCall(mem_read(dctx_a, x));
391: PetscCall(mem_read(dctx_b, y));
392: PetscCall(PetscDeviceContextWaitForContext(dctx_c, dctx_b));
393: // C waited on B, so synchronizing C should remove B from the map but *not* remove A
394: PetscCall(PetscDeviceContextSynchronize(dctx_c));
395: PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a)}));
396: PetscCall(PetscDeviceContextSynchronize(dctx_a));
397: PetscCall(CheckMapEqual({}));
399: // Test that read->write correctly wipes out the map
400: PetscCall(mem_read(dctx_a, x));
401: PetscCall(mem_read(dctx_b, x));
402: PetscCall(mem_read(dctx_c, x));
403: PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a, dctx_b, dctx_c)}));
404: PetscCall(mem_write(dctx_a, x));
405: PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_a)}));
406: PetscCall(PetscDeviceContextSynchronize(dctx_a));
407: PetscCall(CheckMapEqual({}));
409: PetscCall(PetscDeviceContextDestroy(&dctx_a));
410: PetscCall(PetscDeviceContextDestroy(&dctx_b));
411: PetscCall(PetscDeviceContextDestroy(&dctx_c));
413: PetscCall(PetscContainerDestroy(&x));
414: PetscCall(PetscContainerDestroy(&y));
415: PetscCall(PetscContainerDestroy(&z));
416: PetscCall(PetscPrintf(PETSC_COMM_WORLD, "EXIT_SUCCESS\n"));
417: PetscCall(PetscFinalize());
418: return 0;
419: }
420: #else // PETSC_CPP_VERSION > 11
421: int main(int argc, char *argv[])
422: {
423: PetscFunctionBeginUser;
424: PetscCall(PetscInitialize(&argc, &argv, nullptr, help));
425: PetscCall(PetscPrintf(PETSC_COMM_WORLD, "EXIT_SUCCESS\n"));
426: PetscCall(PetscFinalize());
427: return 0;
428: }
429: #endif
431: /*TEST
433: testset:
434: requires: cxx
435: output_file: ./output/ExitSuccess.out
436: test:
437: requires: !device
438: suffix: host_no_device
439: test:
440: requires: device
441: args: -default_device_type host
442: suffix: host_with_device
443: test:
444: requires: cuda
445: args: -default_device_type cuda
446: suffix: cuda
447: test:
448: requires: hip
449: args: -default_device_type hip
450: suffix: hip
451: test:
452: requires: sycl
453: args: -default_device_type sycl
454: suffix: sycl
456: TEST*/