1 #include "petscdevice_interface_internal.hpp" /*I <petscdevice.h> I*/ 2 3 #include <petsc/private/cpp/object_pool.hpp> 4 #include <petsc/private/cpp/utility.hpp> 5 6 #include <unordered_map> 7 #include <algorithm> // std::remove_if(), std::find_if() 8 #include <vector> 9 #include <string> 10 #include <sstream> // std::ostringstream 11 12 #if defined(__clang__) 13 #pragma clang diagnostic push 14 #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 15 #endif 16 17 // ========================================================================================== 18 // PetscEvent 19 // ========================================================================================== 20 21 struct PetscEventAllocator : public Petsc::AllocatorBase<PetscEvent> { 22 PETSC_NODISCARD static PetscErrorCode create(PetscEvent *event) noexcept 23 { 24 PetscFunctionBegin; 25 PetscCall(PetscNew(event)); 26 PetscFunctionReturn(0); 27 } 28 29 PETSC_NODISCARD static PetscErrorCode destroy(PetscEvent event) noexcept 30 { 31 PetscFunctionBegin; 32 PetscCall(reset(event)); 33 PetscCall(PetscFree(event)); 34 PetscFunctionReturn(0); 35 } 36 37 PETSC_NODISCARD static PetscErrorCode reset(PetscEvent event) noexcept 38 { 39 PetscFunctionBegin; 40 if (auto &destroy = event->destroy) { 41 PetscCall((*destroy)(event)); 42 destroy = nullptr; 43 } 44 PetscAssert(!event->data, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Event failed to destroy its data member: %p", event->data); 45 event->dctx_id = 0; 46 event->dctx_state = 0; 47 event->dtype = PETSC_DEVICE_DEFAULT(); 48 PetscFunctionReturn(0); 49 } 50 }; 51 52 static Petsc::ObjectPool<PetscEvent, PetscEventAllocator> event_pool; 53 54 static PetscErrorCode PetscDeviceContextCreateEvent_Private(PetscDeviceContext dctx, PetscEvent *event) 55 { 56 PetscFunctionBegin; 57 PetscValidDeviceContext(dctx, 1); 58 PetscValidPointer(event, 2); 59 PetscCall(event_pool.allocate(event)); 60 PetscCall(PetscDeviceContextGetDeviceType(dctx, &(*event)->dtype)); 61 PetscTryTypeMethod(dctx, createevent, *event); 62 PetscFunctionReturn(0); 63 } 64 65 static PetscErrorCode PetscEventDestroy_Private(PetscEvent *event) 66 { 67 PetscFunctionBegin; 68 PetscValidPointer(event, 1); 69 if (*event) PetscCall(event_pool.deallocate(Petsc::util::exchange(*event, nullptr))); 70 PetscFunctionReturn(0); 71 } 72 73 static PetscErrorCode PetscDeviceContextRecordEvent_Private(PetscDeviceContext dctx, PetscEvent event) 74 { 75 PetscObjectId id; 76 PetscObjectState state; 77 78 PetscFunctionBegin; 79 PetscValidDeviceContext(dctx, 1); 80 PetscValidPointer(event, 2); 81 id = PetscObjectCast(dctx)->id; 82 state = PetscObjectCast(dctx)->state; 83 // technically state can never be less than event->dctx_state (only equal) but we include 84 // it in the check just in case 85 if ((id == event->dctx_id) && (state <= event->dctx_state)) PetscFunctionReturn(0); 86 if (dctx->ops->recordevent) { 87 // REVIEW ME: 88 // TODO maybe move this to impls, as they can determine whether they can interoperate with 89 // other device types more readily 90 if (PetscDefined(USE_DEBUG) && (event->dtype != PETSC_DEVICE_HOST)) { 91 PetscDeviceType dtype; 92 93 PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype)); 94 PetscCheck(event->dtype == dtype, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Event type %s does not match device context type %s", PetscDeviceTypes[event->dtype], PetscDeviceTypes[dtype]); 95 } 96 PetscUseTypeMethod(dctx, recordevent, event); 97 } 98 event->dctx_id = id; 99 event->dctx_state = state; 100 PetscFunctionReturn(0); 101 } 102 103 static PetscErrorCode PetscDeviceContextWaitForEvent_Private(PetscDeviceContext dctx, PetscEvent event) 104 { 105 PetscFunctionBegin; 106 PetscValidDeviceContext(dctx, 1); 107 PetscValidPointer(event, 2); 108 // empty data implies you cannot wait on this event 109 if (!event->data) PetscFunctionReturn(0); 110 if (PetscDefined(USE_DEBUG)) { 111 const auto etype = event->dtype; 112 PetscDeviceType dtype; 113 114 PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype)); 115 PetscCheck(etype == dtype, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Event type %s does not match device context type %s", PetscDeviceTypes[etype], PetscDeviceTypes[dtype]); 116 } 117 if (PetscObjectCast(dctx)->id == event->dctx_id) PetscFunctionReturn(0); 118 PetscTryTypeMethod(dctx, waitforevent, event); 119 PetscFunctionReturn(0); 120 } 121 122 // ========================================================================================== 123 // PetscStackFrame 124 // 125 // A helper class that (when debugging is enabled) contains the stack frame from which 126 // PetscDeviceContextMakrIntentFromID(). It is intended to be derived from, since this enables 127 // empty-base-class optimization to kick in when debugging is disabled. 128 // ========================================================================================== 129 130 template <bool use_debug> 131 struct PetscStackFrame; 132 133 template <> 134 struct PetscStackFrame</* use_debug = */ true> { 135 std::string file{}; 136 std::string function{}; 137 int line{}; 138 139 PetscStackFrame() = default; 140 141 PetscStackFrame(const char *file_, const char *func_, int line_) noexcept : file(split_on_petsc_path_(file_)), function(func_), line(line_) { } 142 143 bool operator==(const PetscStackFrame &other) const noexcept { return line == other.line && file == other.file && function == other.function; } 144 145 PETSC_NODISCARD std::string to_string() const noexcept 146 { 147 std::string ret; 148 149 ret = '(' + function + "() at " + file + ':' + std::to_string(line) + ')'; 150 return ret; 151 } 152 153 private: 154 static std::string split_on_petsc_path_(std::string &&in) noexcept 155 { 156 auto pos = in.find("petsc/src"); 157 158 if (pos == std::string::npos) pos = in.find("petsc/include"); 159 if (pos == std::string::npos) pos = 0; 160 return in.substr(pos); 161 } 162 163 friend std::ostream &operator<<(std::ostream &os, const PetscStackFrame &frame) 164 { 165 os << frame.to_string(); 166 return os; 167 } 168 169 friend void swap(PetscStackFrame &lhs, PetscStackFrame &rhs) noexcept 170 { 171 using std::swap; 172 173 swap(lhs.file, rhs.file); 174 swap(lhs.function, rhs.function); 175 swap(lhs.line, rhs.line); 176 } 177 }; 178 179 template <> 180 struct PetscStackFrame</* use_debug = */ false> { 181 template <typename... T> 182 constexpr PetscStackFrame(T &&...) noexcept 183 { 184 } 185 186 constexpr bool operator==(const PetscStackFrame &) const noexcept { return true; } 187 188 PETSC_NODISCARD static std::string to_string() noexcept { return "(unknown)"; } 189 190 friend std::ostream &operator<<(std::ostream &os, const PetscStackFrame &) noexcept 191 { 192 os << "(unknown)"; 193 return os; 194 } 195 }; 196 197 // ========================================================================================== 198 // MarkedObjectMap 199 // 200 // A mapping from a PetscObjectId to a PetscEvent and (if debugging is enabled) a 201 // PetscStackFrame containing the location where PetscDeviceContextMarkIntentFromID was called 202 // ========================================================================================== 203 204 class MarkedObjectMap : public Petsc::RegisterFinalizeable<MarkedObjectMap> { 205 public: 206 // Note we derive from PetscStackFrame so that the empty base class optimization can kick 207 // in. If it were just a member it would still take up storage in optimized builds 208 class snapshot_type : private PetscStackFrame<PetscDefined(USE_DEBUG)> { 209 public: 210 using frame_type = PetscStackFrame<PetscDefined(USE_DEBUG)>; 211 212 snapshot_type() = default; 213 snapshot_type(PetscDeviceContext, frame_type) noexcept; 214 215 ~snapshot_type() noexcept; 216 217 // movable 218 snapshot_type(snapshot_type &&) noexcept; 219 snapshot_type &operator=(snapshot_type &&) noexcept; 220 221 // not copyable 222 snapshot_type(const snapshot_type &) noexcept = delete; 223 snapshot_type &operator=(const snapshot_type &) noexcept = delete; 224 225 PETSC_NODISCARD PetscEvent event() const noexcept { return event_; } 226 PETSC_NODISCARD const frame_type &frame() const noexcept { return *this; } 227 PETSC_NODISCARD frame_type &frame() noexcept { return *this; } 228 229 PETSC_NODISCARD PetscObjectId dctx_id() const noexcept 230 { 231 PetscFunctionBegin; 232 PetscAssertAbort(event(), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Snapshot %s does not contain an event!", frame().to_string().c_str()); 233 PetscFunctionReturn(event()->dctx_id); 234 } 235 236 PETSC_NODISCARD PetscErrorCode ensure_event(PetscDeviceContext) noexcept; 237 238 friend void swap(snapshot_type &, snapshot_type &) noexcept; 239 240 private: 241 PetscEvent event_{}; // the state of device context when this snapshot was recorded 242 243 PETSC_NODISCARD static PetscEvent init_event_(PetscDeviceContext) noexcept; 244 }; 245 246 // the "value" each key maps to 247 struct mapped_type { 248 using dependency_type = std::vector<snapshot_type>; 249 250 PetscMemoryAccessMode mode = PETSC_MEMORY_ACCESS_READ; 251 snapshot_type last_write{}; 252 dependency_type dependencies{}; 253 }; 254 255 using map_type = std::unordered_map<PetscObjectId, mapped_type>; 256 257 map_type map; 258 259 private: 260 friend RegisterFinalizeable; 261 262 PETSC_NODISCARD PetscErrorCode finalize_() noexcept; 263 }; 264 265 // ========================================================================================== 266 // MarkedObejctMap Private API 267 // ========================================================================================== 268 269 inline PetscErrorCode MarkedObjectMap::finalize_() noexcept 270 { 271 PetscFunctionBegin; 272 PetscCall(PetscInfo(nullptr, "Finalizing marked object map\n")); 273 map.clear(); 274 PetscFunctionReturn(0); 275 } 276 277 // ========================================================================================== 278 // MarkedObejctMap::snapshot_type Private API 279 // ========================================================================================== 280 281 inline PetscEvent MarkedObjectMap::snapshot_type::init_event_(PetscDeviceContext dctx) noexcept 282 { 283 PetscEvent event = nullptr; 284 285 PetscFunctionBegin; 286 PetscCallAbort(PETSC_COMM_SELF, PetscDeviceContextCreateEvent_Private(dctx, &event)); 287 PetscCallAbort(PETSC_COMM_SELF, PetscDeviceContextRecordEvent_Private(dctx, event)); 288 PetscFunctionReturn(event); 289 } 290 291 // ========================================================================================== 292 // MarkedObejctMap::snapshot_type Public API 293 // ========================================================================================== 294 295 MarkedObjectMap::snapshot_type::snapshot_type(PetscDeviceContext dctx, frame_type frame) noexcept : frame_type(std::move(frame)), event_(init_event_(dctx)) { } 296 297 MarkedObjectMap::snapshot_type::~snapshot_type() noexcept 298 { 299 PetscFunctionBegin; 300 PetscCallAbort(PETSC_COMM_SELF, PetscEventDestroy_Private(&event_)); 301 PetscFunctionReturnVoid(); 302 } 303 304 // movable 305 MarkedObjectMap::snapshot_type::snapshot_type(snapshot_type &&other) noexcept : frame_type(std::move(other)), event_(Petsc::util::exchange(other.event_, nullptr)) { } 306 307 MarkedObjectMap::snapshot_type &MarkedObjectMap::snapshot_type::operator=(snapshot_type &&other) noexcept 308 { 309 PetscFunctionBegin; 310 if (this != &other) { 311 frame_type::operator=(std::move(other)); 312 PetscCallAbort(PETSC_COMM_SELF, PetscEventDestroy_Private(&event_)); 313 event_ = Petsc::util::exchange(other.event_, nullptr); 314 } 315 PetscFunctionReturn(*this); 316 } 317 318 PetscErrorCode MarkedObjectMap::snapshot_type::ensure_event(PetscDeviceContext dctx) noexcept 319 { 320 PetscFunctionBegin; 321 if (PetscUnlikely(!event_)) PetscCall(PetscDeviceContextCreateEvent_Private(dctx, &event_)); 322 PetscFunctionReturn(0); 323 } 324 325 void swap(MarkedObjectMap::snapshot_type &lhs, MarkedObjectMap::snapshot_type &rhs) noexcept 326 { 327 using std::swap; 328 329 swap(lhs.frame(), rhs.frame()); 330 swap(lhs.event_, rhs.event_); 331 } 332 333 // A mapping between PetscObjectId (i.e. some PetscObject) to the list of PetscEvent's encoding 334 // the last time the PetscObject was accessed 335 static MarkedObjectMap marked_object_map; 336 337 // ========================================================================================== 338 // Utility Functions 339 // ========================================================================================== 340 341 PetscErrorCode PetscGetMarkedObjectMap_Internal(std::size_t *nkeys, PetscObjectId **keys, PetscMemoryAccessMode **modes, std::size_t **ndeps, PetscEvent ***dependencies) 342 { 343 std::size_t i = 0; 344 const auto &map = marked_object_map.map; 345 const auto size = *nkeys = map.size(); 346 347 PetscFunctionBegin; 348 PetscCall(PetscMalloc4(size, keys, size, modes, size, ndeps, size, dependencies)); 349 for (auto &&it : map) { 350 std::size_t j = 0; 351 352 (*keys)[i] = it.first; 353 (*modes)[i] = it.second.mode; 354 (*ndeps)[i] = it.second.dependencies.size(); 355 (*dependencies)[i] = nullptr; 356 PetscCall(PetscMalloc1((*ndeps)[i], (*dependencies) + i)); 357 for (auto &&dep : it.second.dependencies) (*dependencies)[i][j++] = dep.event(); 358 ++i; 359 } 360 PetscFunctionReturn(0); 361 } 362 363 PetscErrorCode PetscRestoreMarkedObjectMap_Internal(std::size_t nkeys, PetscObjectId **keys, PetscMemoryAccessMode **modes, std::size_t **ndeps, PetscEvent ***dependencies) 364 { 365 PetscFunctionBegin; 366 for (std::size_t i = 0; i < nkeys; ++i) PetscCall(PetscFree((*dependencies)[i])); 367 PetscCall(PetscFree4(*keys, *modes, *ndeps, *dependencies)); 368 PetscFunctionReturn(0); 369 } 370 371 template <typename T> 372 static PetscErrorCode PetscDeviceContextMapIterVisitor(PetscDeviceContext dctx, T &&callback) noexcept 373 { 374 const auto dctx_id = PetscObjectCast(dctx)->id; 375 auto &dctx_deps = CxxDataCast(dctx)->deps; 376 auto &object_map = marked_object_map.map; 377 378 PetscFunctionBegin; 379 for (auto &&dep : dctx_deps) { 380 const auto mapit = object_map.find(dep); 381 382 // Need this check since the final PetscDeviceContext may run through this *after* the map 383 // has been finalized (and cleared), and hence might fail to find its dependencies. This is 384 // perfectly valid since the user no longer cares about dangling dependencies after PETSc 385 // is finalized 386 if (PetscLikely(mapit != object_map.end())) { 387 auto &deps = mapit->second.dependencies; 388 const auto end = deps.end(); 389 const auto it = std::remove_if(deps.begin(), end, [&](const MarkedObjectMap::snapshot_type &obj) { return obj.dctx_id() == dctx_id; }); 390 391 PetscCall(callback(mapit, deps.cbegin(), static_cast<decltype(deps.cend())>(it))); 392 // remove ourselves 393 PetscCallCXX(deps.erase(it, end)); 394 // continue to next object, but erase this one if it has no more dependencies 395 if (deps.empty()) PetscCallCXX(object_map.erase(mapit)); 396 } 397 } 398 PetscCallCXX(dctx_deps.clear()); 399 PetscFunctionReturn(0); 400 } 401 402 PetscErrorCode PetscDeviceContextSyncClearMap_Internal(PetscDeviceContext dctx) 403 { 404 using map_iterator = MarkedObjectMap::map_type::const_iterator; 405 using dep_iterator = MarkedObjectMap::mapped_type::dependency_type::const_iterator; 406 407 PetscFunctionBegin; 408 PetscCall(PetscDeviceContextMapIterVisitor(dctx, [&](map_iterator mapit, dep_iterator it, dep_iterator end) { 409 PetscFunctionBegin; 410 if (PetscDefined(USE_DEBUG_AND_INFO)) { 411 std::ostringstream oss; 412 const auto mode = PetscMemoryAccessModeToString(mapit->second.mode); 413 414 oss << "synced dctx " << PetscObjectCast(dctx)->id << ", remaining leaves for obj " << mapit->first << ": {"; 415 while (it != end) { 416 oss << "[dctx " << it->dctx_id() << ", " << mode << ' ' << it->frame() << ']'; 417 if (++it != end) oss << ", "; 418 } 419 oss << '}'; 420 PetscCall(PetscInfo(nullptr, "%s\n", oss.str().c_str())); 421 } 422 PetscFunctionReturn(0); 423 })); 424 { 425 // the recursive sync clear map call is unbounded in case of a dependenct loop so we make a 426 // copy 427 // clang-format off 428 const std::vector<CxxData::upstream_type::value_type> upstream_copy( 429 std::make_move_iterator(CxxDataCast(dctx)->upstream.begin()), 430 std::make_move_iterator(CxxDataCast(dctx)->upstream.end()) 431 ); 432 // clang-format on 433 434 // aftermath, clear our set of parents (to avoid infinite recursion) and mark ourselves as no 435 // longer contained (while the empty graph technically *is* always contained, it is not what 436 // we mean by it) 437 PetscCall(CxxDataCast(dctx)->clear()); 438 //dctx->contained = PETSC_FALSE; 439 for (auto &&upstrm : upstream_copy) { 440 // check that this parent still points to what we originally thought it was 441 PetscCheck(upstrm.second.id == PetscObjectCast(upstrm.first)->id, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Upstream dctx %" PetscInt64_FMT " no longer exists, now has id %" PetscInt64_FMT, upstrm.second.id, PetscObjectCast(upstrm.first)->id); 442 PetscCall(PetscDeviceContextSyncClearMap_Internal(upstrm.first)); 443 } 444 } 445 PetscFunctionReturn(0); 446 } 447 448 PetscErrorCode PetscDeviceContextCheckNotOrphaned_Internal(PetscDeviceContext dctx) 449 { 450 std::ostringstream oss; 451 //const auto allow = dctx->options.allow_orphans, contained = dctx->contained; 452 const auto allow = true, contained = true; 453 auto wrote_to_oss = false; 454 using map_iterator = MarkedObjectMap::map_type::const_iterator; 455 using dep_iterator = MarkedObjectMap::mapped_type::dependency_type::const_iterator; 456 457 PetscFunctionBegin; 458 PetscCall(PetscDeviceContextMapIterVisitor(dctx, [&](map_iterator mapit, dep_iterator it, dep_iterator end) { 459 PetscFunctionBegin; 460 if (allow || contained) PetscFunctionReturn(0); 461 wrote_to_oss = true; 462 oss << "- PetscObject (id " << mapit->first << "), intent " << PetscMemoryAccessModeToString(mapit->second.mode) << ' ' << it->frame(); 463 if (std::distance(it, end) == 0) oss << " (orphaned)"; // we were the only dependency 464 oss << '\n'; 465 PetscFunctionReturn(0); 466 })); 467 PetscCheck(!wrote_to_oss, PETSC_COMM_SELF, PETSC_ERR_ORDER, "Destroying PetscDeviceContext ('%s', id %" PetscInt64_FMT ") would leave the following dangling (possibly orphaned) dependants:\n%s\nMust synchronize before destroying it, or allow it to be destroyed with orphans", 468 PetscObjectCast(dctx)->name ? PetscObjectCast(dctx)->name : "unnamed", PetscObjectCast(dctx)->id, oss.str().c_str()); 469 PetscCall(CxxDataCast(dctx)->clear()); 470 PetscFunctionReturn(0); 471 } 472 473 #define DEBUG_INFO(mess, ...) PetscDebugInfo(dctx, "dctx %" PetscInt64_FMT " (%s) - obj %" PetscInt64_FMT " (%s): " mess, PetscObjectCast(dctx)->id, PetscObjectCast(dctx)->name ? PetscObjectCast(dctx)->name : "unnamed", id, name, ##__VA_ARGS__) 474 475 // The current mode is compatible with the previous mode (i.e. read-read) so we need only 476 // update the existing version and possibly appeand ourselves to the dependency list 477 478 template <bool use_debug> 479 static PetscErrorCode MarkFromID_CompatibleModes(MarkedObjectMap::mapped_type &marked, PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> &frame, const char *PETSC_UNUSED name, bool *update_object_dependencies) 480 { 481 const auto dctx_id = PetscObjectCast(dctx)->id; 482 auto &object_dependencies = marked.dependencies; 483 const auto end = object_dependencies.end(); 484 const auto it = std::find_if(object_dependencies.begin(), end, [&](const MarkedObjectMap::snapshot_type &obj) { return obj.dctx_id() == dctx_id; }); 485 486 PetscFunctionBegin; 487 PetscCall(DEBUG_INFO("new mode (%s) COMPATIBLE with %s mode (%s), no need to serialize\n", PetscMemoryAccessModeToString(mode), object_dependencies.empty() ? "default" : "old", PetscMemoryAccessModeToString(marked.mode))); 488 if (it != end) { 489 using std::swap; 490 491 // we have been here before, all we must do is update our entry then we can bail 492 PetscCall(DEBUG_INFO("found old self as dependency, updating\n")); 493 PetscAssert(CxxDataCast(dctx)->deps.find(id) != CxxDataCast(dctx)->deps.end(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "PetscDeviceContext %" PetscInt64_FMT " listed as dependency for object %" PetscInt64_FMT " (%s), but does not have the object in private dependency list!", dctx_id, id, name); 494 swap(it->frame(), frame); 495 PetscCall(PetscDeviceContextRecordEvent_Private(dctx, it->event())); 496 *update_object_dependencies = false; 497 PetscFunctionReturn(0); 498 } 499 500 // we have not been here before, need to serialize with the last write event (if it exists) 501 // and add ourselves to the dependency list 502 if (const auto event = marked.last_write.event()) PetscCall(PetscDeviceContextWaitForEvent_Private(dctx, event)); 503 PetscFunctionReturn(0); 504 } 505 506 template <bool use_debug> 507 static PetscErrorCode MarkFromID_IncompatibleModes_UpdateLastWrite(MarkedObjectMap::mapped_type &marked, PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> &frame, const char *PETSC_UNUSED name, bool *update_object_dependencies) 508 { 509 const auto dctx_id = PetscObjectCast(dctx)->id; 510 auto &last_write = marked.last_write; 511 auto &last_dep = marked.dependencies.back(); 512 PetscDeviceType dtype; 513 514 PetscFunctionBegin; 515 PetscAssert(marked.dependencies.size() == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Can only have a single writer as dependency, have %zu!", marked.dependencies.size()); 516 PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype)); 517 if (last_dep.event()->dtype != dtype) { 518 PetscCall(DEBUG_INFO("moving last write dependency (intent %s)\n", PetscMemoryAccessModeToString(marked.mode))); 519 last_write = std::move(last_dep); 520 PetscFunctionReturn(0); 521 } 522 523 // we match the device type of the dependency, we can reuse its event! 524 auto &dctx_upstream_deps = CxxDataCast(dctx)->deps; 525 const auto last_write_was_also_us = last_write.event() && (last_write.dctx_id() == dctx_id); 526 using std::swap; 527 528 PetscCall(DEBUG_INFO("we matched the previous write dependency's (intent %s) device type (%s), swapping last dependency with last write\n", PetscMemoryAccessModeToString(marked.mode), PetscDeviceTypes[dtype])); 529 if (last_dep.event()->dctx_id != dctx_id) dctx_upstream_deps.emplace(id); 530 PetscAssert(dctx_upstream_deps.find(id) != dctx_upstream_deps.end(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not find id %" PetscInt64_FMT "in object dependencies, but we have apparently recorded the last dependency %s!", id, 531 last_write.frame().to_string().c_str()); 532 swap(last_write, last_dep); 533 if (last_write_was_also_us) { 534 PetscCall(DEBUG_INFO("we were also the last write event (intent %s), updating\n", PetscMemoryAccessModeToString(mode))); 535 // we are both the last to write *and* the last to leave a write event. This is the 536 // fast path, we only need to update the frame and update the recorded event 537 swap(last_dep.frame(), frame); 538 // last used to be last_write which is not guaranteed to have an event, so must 539 // create it now 540 PetscCall(last_dep.ensure_event(dctx)); 541 PetscCall(PetscDeviceContextRecordEvent_Private(dctx, last_dep.event())); 542 *update_object_dependencies = false; 543 } 544 PetscFunctionReturn(0); 545 } 546 547 // The current mode is NOT compatible with the previous mode. We must serialize with all events 548 // in the dependency list, possibly clear it, and update the previous write event 549 550 template <bool use_debug> 551 static PetscErrorCode MarkFromID_IncompatibleModes(MarkedObjectMap::mapped_type &marked, PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> &frame, const char *name, bool *update_object_dependencies) 552 { 553 auto &old_mode = marked.mode; 554 auto &object_dependencies = marked.dependencies; 555 556 PetscFunctionBegin; 557 // we are NOT compatible with the previous mode 558 PetscCall(DEBUG_INFO("new mode (%s) NOT COMPATIBLE with %s mode (%s), serializing then clearing (%zu) %s\n", PetscMemoryAccessModeToString(mode), object_dependencies.empty() ? "default" : "old", PetscMemoryAccessModeToString(old_mode), 559 object_dependencies.size(), object_dependencies.size() == 1 ? "dependency" : "dependencies")); 560 561 for (const auto &dep : object_dependencies) PetscCall(PetscDeviceContextWaitForEvent_Private(dctx, dep.event())); 562 // if the previous mode wrote, update the last write node with it 563 if (PetscMemoryAccessWrite(old_mode)) PetscCall(MarkFromID_IncompatibleModes_UpdateLastWrite(marked, dctx, id, mode, frame, name, update_object_dependencies)); 564 565 old_mode = mode; 566 // clear out the old dependencies if are about to append ourselves 567 if (*update_object_dependencies) object_dependencies.clear(); 568 PetscFunctionReturn(0); 569 } 570 571 template <bool use_debug> 572 static PetscErrorCode PetscDeviceContextMarkIntentFromID_Private(PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> frame, const char *name) 573 { 574 auto &marked = marked_object_map.map[id]; 575 auto &object_dependencies = marked.dependencies; 576 auto update_object_dependencies = true; 577 578 PetscFunctionBegin; 579 if ((marked.mode == PETSC_MEMORY_ACCESS_READ) && (mode == PETSC_MEMORY_ACCESS_READ)) { 580 PetscCall(MarkFromID_CompatibleModes(marked, dctx, id, mode, frame, name, &update_object_dependencies)); 581 } else { 582 PetscCall(MarkFromID_IncompatibleModes(marked, dctx, id, mode, frame, name, &update_object_dependencies)); 583 } 584 if (update_object_dependencies) { 585 // become the new leaf by appending ourselves 586 PetscCall(DEBUG_INFO("%s with intent %s\n", object_dependencies.empty() ? "dependency list is empty, creating new leaf" : "appending to existing leaves", PetscMemoryAccessModeToString(mode))); 587 PetscCallCXX(object_dependencies.emplace_back(dctx, std::move(frame))); 588 PetscCallCXX(CxxDataCast(dctx)->deps.emplace(id)); 589 } 590 PetscFunctionReturn(0); 591 } 592 593 #undef DEBUG_INFO 594 595 /*@C 596 PetscDeviceContextMarkIntentFromID - Indicate a `PetscDeviceContext`s access intent to the 597 auto-dependency system 598 599 Not Collective 600 601 Input Parameters: 602 + dctx - The `PetscDeviceContext` 603 . id - The `PetscObjectId` to mark 604 . mode - The desired access intent 605 - name - The object name (for debug purposes, ignored in optimized builds) 606 607 Notes: 608 This routine formally informs the dependency system that `dctx` will access the object 609 represented by `id` with `mode` and adds `dctx` to `id`'s list of dependencies (termed 610 "leaves"). 611 612 If the existing set of leaves have an incompatible `PetscMemoryAccessMode` to `mode`, `dctx` 613 will be serialized against them. 614 615 Level: intermediate 616 617 .seealso: `PetscDeviceContextWaitForContext()`, `PetscDeviceContextSynchronize()`, 618 `PetscObjectGetId()`, `PetscMemoryAccessMode` 619 @*/ 620 PetscErrorCode PetscDeviceContextMarkIntentFromID(PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, const char name[]) 621 { 622 #if PetscDefined(USE_DEBUG) 623 const auto index = petscstack.currentsize > 2 ? petscstack.currentsize - 2 : 0; 624 const auto file = petscstack.file[index]; 625 const auto function = petscstack.function[index]; 626 const auto line = petscstack.line[index]; 627 #else 628 constexpr const char *file = nullptr; 629 constexpr const char *function = nullptr; 630 constexpr auto line = 0; 631 #endif 632 633 PetscFunctionBegin; 634 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 635 if (name) PetscValidCharPointer(name, 4); 636 PetscCall(marked_object_map.register_finalize()); 637 PetscCall(PetscLogEventBegin(DCONTEXT_Mark, dctx, nullptr, nullptr, nullptr)); 638 PetscCall(PetscDeviceContextMarkIntentFromID_Private(dctx, id, mode, MarkedObjectMap::snapshot_type::frame_type{file, function, line}, name ? name : "unknown object")); 639 PetscCall(PetscLogEventEnd(DCONTEXT_Mark, dctx, nullptr, nullptr, nullptr)); 640 PetscFunctionReturn(0); 641 } 642 643 #if defined(__clang__) 644 #pragma clang diagnostic pop 645 #endif 646