1 #include "petscdevice_interface_internal.hpp" /*I <petscdevice.h> I*/ 2 #include <petsc/private/viewerimpl.h> // _p_PetscViewer for PetscObjectCast() 3 4 #include <petsc/private/cpp/object_pool.hpp> 5 #include <petsc/private/cpp/utility.hpp> 6 #include <petsc/private/cpp/array.hpp> 7 8 #include <vector> 9 #include <string> // std::to_string among other things 10 11 /* Define the allocator */ 12 class PetscDeviceContextAllocator : public Petsc::AllocatorBase<PetscDeviceContext> { 13 public: 14 PETSC_CXX_COMPAT_DECL(PetscErrorCode create(PetscDeviceContext *dctx)) { 15 PetscFunctionBegin; 16 PetscCall(PetscHeaderCreate(*dctx, PETSC_DEVICE_CONTEXT_CLASSID, "PetscDeviceContext", "PetscDeviceContext", "Sys", PETSC_COMM_SELF, PetscDeviceContextDestroy, PetscDeviceContextView)); 17 PetscCallCXX(PetscObjectCast(*dctx)->cpp = new CxxData()); 18 PetscCall(reset(*dctx, false)); 19 PetscFunctionReturn(0); 20 } 21 22 PETSC_CXX_COMPAT_DECL(PetscErrorCode destroy(PetscDeviceContext dctx)) { 23 PetscFunctionBegin; 24 PetscAssert(!dctx->numChildren, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Device context still has %" PetscInt_FMT " un-joined children, must call PetscDeviceContextJoin() with all children before destroying", dctx->numChildren); 25 PetscTryTypeMethod(dctx, destroy); 26 PetscCall(PetscDeviceDestroy(&dctx->device)); 27 PetscCall(PetscFree(dctx->childIDs)); 28 delete CxxDataCast(dctx); 29 PetscCall(PetscHeaderDestroy(&dctx)); 30 PetscFunctionReturn(0); 31 } 32 33 PETSC_CXX_COMPAT_DECL(PetscErrorCode reset(PetscDeviceContext dctx, bool zero = true)) { 34 PetscFunctionBegin; 35 if (zero) { 36 // reset the device if the user set it 37 if (auto &userset = dctx->usersetdevice) { 38 userset = PETSC_FALSE; 39 PetscTryTypeMethod(dctx, destroy); 40 PetscCall(PetscDeviceDestroy(&dctx->device)); 41 PetscCall(PetscArrayzero(dctx->ops, 1)); 42 dctx->data = nullptr; 43 } 44 PetscCall(PetscHeaderReset_Internal(PetscObjectCast(dctx))); 45 dctx->numChildren = 0; 46 dctx->setup = PETSC_FALSE; 47 // don't deallocate the child array, rather just zero it out 48 PetscCall(PetscArrayzero(dctx->childIDs, dctx->maxNumChildren)); 49 PetscCall(CxxDataCast(dctx)->clear()); 50 } 51 dctx->streamType = PETSC_STREAM_DEFAULT_BLOCKING; 52 PetscFunctionReturn(0); 53 } 54 }; 55 56 static Petsc::ObjectPool<PetscDeviceContext, PetscDeviceContextAllocator> contextPool; 57 58 /*@C 59 PetscDeviceContextCreate - Creates a `PetscDeviceContext` 60 61 Not Collective 62 63 Output Paramemter: 64 . dctx - The `PetscDeviceContext` 65 66 Note: 67 Unlike almost every other PETSc class it is advised that most users use 68 `PetscDeviceContextDuplicate()` rather than this routine to create new contexts. Contexts of 69 different types are incompatible with one another; using `PetscDeviceContextDuplicate()` 70 ensures compatible types. 71 72 DAG representation: 73 .vb 74 time -> 75 76 |= CALL =| - dctx -> 77 .ve 78 79 Level: beginner 80 81 .N ASYNC_API 82 83 .seealso: `PetscDeviceContextDuplicate()`, `PetscDeviceContextSetDevice()`, 84 `PetscDeviceContextSetStreamType()`, `PetscDeviceContextSetUp()`, 85 `PetscDeviceContextSetFromOptions()`, `PetscDeviceContextView()`, `PetscDeviceContextDestroy()` 86 @*/ 87 PetscErrorCode PetscDeviceContextCreate(PetscDeviceContext *dctx) { 88 PetscFunctionBegin; 89 PetscValidPointer(dctx, 1); 90 PetscCall(PetscDeviceInitializePackage()); 91 PetscCall(PetscLogEventBegin(DCONTEXT_Create, nullptr, nullptr, nullptr, nullptr)); 92 PetscCall(contextPool.allocate(dctx)); 93 PetscCall(PetscLogEventEnd(DCONTEXT_Create, nullptr, nullptr, nullptr, nullptr)); 94 PetscFunctionReturn(0); 95 } 96 97 /*@C 98 PetscDeviceContextDestroy - Frees a `PetscDeviceContext` 99 100 Not Collective 101 102 Input Parameters: 103 . dctx - The `PetscDeviceContext` 104 105 Notes: 106 No implicit synchronization occurs due to this routine, all resources are released completely 107 asynchronously w.r.t. the host. If one needs to guarantee access to the data produced on 108 `dctx`'s stream the user is responsible for calling `PetscDeviceContextSynchronize()` before 109 calling this routine. 110 111 DAG represetation: 112 .vb 113 time -> 114 115 -> dctx - |= CALL =| 116 .ve 117 118 Developer Notes: 119 `dctx` is never actually "destroyed" in the classical sense. It is returned to an ever 120 growing pool of `PetscDeviceContext`s. There are currently no limits on the size of the pool, 121 this should perhaps be implemented. 122 123 Level: beginner 124 125 .N ASYNC_API 126 127 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextSetDevice()`, 128 `PetscDeviceContextSetUp()`, `PetscDeviceContextSynchronize()` 129 @*/ 130 PetscErrorCode PetscDeviceContextDestroy(PetscDeviceContext *dctx) { 131 PetscFunctionBegin; 132 PetscValidPointer(dctx, 1); 133 if (!*dctx) PetscFunctionReturn(0); 134 PetscCall(PetscLogEventBegin(DCONTEXT_Destroy, nullptr, nullptr, nullptr, nullptr)); 135 if (--(PetscObjectCast(*dctx)->refct) <= 0) { 136 PetscCall(PetscDeviceContextCheckNotOrphaned_Internal(*dctx)); 137 // std::move of the expression of the trivially-copyable type 'PetscDeviceContext' (aka 138 // '_n_PetscDeviceContext *') has no effect; remove std::move() [performance-move-const-arg] 139 // can't remove std::move, since reclaim only takes r-value reference 140 PetscCall(contextPool.deallocate(std::move(*dctx))); // NOLINT (performance-move-const-arg) 141 } 142 PetscCall(PetscLogEventEnd(DCONTEXT_Destroy, nullptr, nullptr, nullptr, nullptr)); 143 *dctx = nullptr; 144 PetscFunctionReturn(0); 145 } 146 147 /*@C 148 PetscDeviceContextSetStreamType - Set the implementation type of the underlying stream for a 149 `PetscDeviceContext` 150 151 Not Collective 152 153 Input Parameters: 154 + dctx - The `PetscDeviceContext` 155 - type - The `PetscStreamType` 156 157 Notes: 158 See `PetscStreamType` in `include/petscdevicetypes.h` for more information on the available 159 types and their interactions. If the `PetscDeviceContext` was previously set up and stream 160 type was changed, you must call `PetscDeviceContextSetUp()` again after this routine. 161 162 Level: beginner 163 164 .seealso: `PetscStreamType`, `PetscDeviceContextGetStreamType()`, `PetscDeviceContextCreate()`, 165 `PetscDeviceContextSetUp()`, `PetscDeviceContextSetFromOptions()` 166 @*/ 167 PetscErrorCode PetscDeviceContextSetStreamType(PetscDeviceContext dctx, PetscStreamType type) { 168 PetscFunctionBegin; 169 // do not use getoptionalnullcontext here since we do not want the user to change the stream 170 // type 171 PetscValidDeviceContext(dctx, 1); 172 PetscValidStreamType(type, 2); 173 // only need to do complex swapping if the object has already been setup 174 if (dctx->setup && (dctx->streamType != type)) { 175 dctx->setup = PETSC_FALSE; 176 PetscCall(PetscLogEventBegin(DCONTEXT_ChangeStream, dctx, nullptr, nullptr, nullptr)); 177 PetscUseTypeMethod(dctx, changestreamtype, type); 178 PetscCall(PetscLogEventEnd(DCONTEXT_ChangeStream, dctx, nullptr, nullptr, nullptr)); 179 } 180 dctx->streamType = type; 181 PetscFunctionReturn(0); 182 } 183 184 /*@C 185 PetscDeviceContextGetStreamType - Get the implementation type of the underlying stream for a 186 `PetscDeviceContext` 187 188 Not Collective 189 190 Input Parameter: 191 . dctx - The `PetscDeviceContext` 192 193 Output Parameter: 194 . type - The `PetscStreamType` 195 196 Notes: 197 See `PetscStreamType` in `include/petscdevicetypes.h` for more information on the available 198 types and their interactions 199 200 Level: beginner 201 202 .seealso: `PetscDeviceContextSetStreamType()`, `PetscDeviceContextCreate()`, 203 `PetscDeviceContextSetFromOptions()` 204 @*/ 205 PetscErrorCode PetscDeviceContextGetStreamType(PetscDeviceContext dctx, PetscStreamType *type) { 206 PetscFunctionBegin; 207 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 208 PetscValidIntPointer(type, 2); 209 *type = dctx->streamType; 210 PetscFunctionReturn(0); 211 } 212 213 /* 214 Actual function to set the device. 215 216 1. Repeatedly destroying and recreating internal data structures (like streams and events) 217 for recycled PetscDeviceContexts is not free. If done often, it does add up. 218 2. The vast majority of PetscDeviceContexts are created by PETSc either as children or 219 default contexts. The default contexts *never* change type, and the chilren are extremely 220 unlikely to (chances are if you fork once, you will fork again very soon). 221 3. The only time this calculus changes is if the user themselves sets the device type. In 222 this case we do not know what the user has changed, so must always wipe the slate clean. 223 224 Thus we need to keep track whether the user explicitly sets the device contexts device. 225 */ 226 static PetscErrorCode PetscDeviceContextSetDevice_Private(PetscDeviceContext dctx, PetscDevice device, PetscBool user_set) { 227 PetscFunctionBegin; 228 // do not use getoptionalnullcontext here since we do not want the user to change its device 229 PetscValidDeviceContext(dctx, 1); 230 PetscValidDevice(device, 2); 231 if (dctx->device && (dctx->device->id == device->id)) PetscFunctionReturn(0); 232 PetscCall(PetscLogEventBegin(DCONTEXT_SetDevice, dctx, nullptr, nullptr, nullptr)); 233 if (const auto destroy = dctx->ops->destroy) PetscCall((*destroy)(dctx)); 234 PetscCall(PetscDeviceDestroy(&dctx->device)); 235 PetscCall(PetscMemzero(dctx->ops, sizeof(*dctx->ops))); 236 PetscCall((*device->ops->createcontext)(dctx)); 237 PetscCall(PetscLogEventEnd(DCONTEXT_SetDevice, dctx, nullptr, nullptr, nullptr)); 238 PetscCall(PetscDeviceReference_Internal(device)); 239 dctx->device = device; 240 dctx->setup = PETSC_FALSE; 241 dctx->usersetdevice = user_set; 242 PetscFunctionReturn(0); 243 } 244 245 PetscErrorCode PetscDeviceContextSetDefaultDeviceForType_Internal(PetscDeviceContext dctx, PetscDeviceType type) { 246 PetscDevice device; 247 248 PetscFunctionBegin; 249 PetscCall(PetscDeviceGetDefaultForType_Internal(type, &device)); 250 PetscCall(PetscDeviceContextSetDevice_Private(dctx, device, PETSC_FALSE)); 251 PetscFunctionReturn(0); 252 } 253 254 /*@C 255 PetscDeviceContextSetDevice - Set the underlying `PetscDevice` for a `PetscDeviceContext` 256 257 Not Collective 258 259 Input Parameters: 260 + dctx - The `PetscDeviceContext` 261 - device - The `PetscDevice` 262 263 Notes: 264 This routine is effectively `PetscDeviceContext`'s "set-type" (so every `PetscDeviceContext` must 265 also have an attached `PetscDevice`). Unlike the usual set-type semantics, it is not stricly 266 necessary to set a contexts device to enable usage, any created `PetscDeviceContext`s will 267 always come equipped with the "default" device. 268 269 This routine is a no-op if `device` is already attached to `dctx`. 270 271 This routine may (but is very unlikely to) initialize the backend device and may incur 272 synchronization. 273 274 Level: intermediate 275 276 .seealso: `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceContextGetDevice()`, 277 `PetscDeviceContextGetDeviceType()` 278 @*/ 279 PetscErrorCode PetscDeviceContextSetDevice(PetscDeviceContext dctx, PetscDevice device) { 280 PetscFunctionBegin; 281 PetscCall(PetscDeviceContextSetDevice_Private(dctx, device, PETSC_TRUE)); 282 PetscFunctionReturn(0); 283 } 284 285 /*@C 286 PetscDeviceContextGetDevice - Get the underlying `PetscDevice` for a `PetscDeviceContext` 287 288 Not Collective 289 290 Input Parameter: 291 . dctx - the `PetscDeviceContext` 292 293 Output Parameter: 294 . device - The `PetscDevice` 295 296 Notes: 297 This is a borrowed reference, the user should not destroy `device`. 298 299 Level: intermediate 300 301 .seealso: `PetscDeviceContextSetDevice()`, `PetscDevice`, `PetscDeviceContextGetDeviceType()` 302 @*/ 303 PetscErrorCode PetscDeviceContextGetDevice(PetscDeviceContext dctx, PetscDevice *device) { 304 PetscFunctionBegin; 305 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 306 PetscValidPointer(device, 2); 307 PetscAssert(dctx->device, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "PetscDeviceContext %" PetscInt64_FMT " has no attached PetscDevice to get", PetscObjectCast(dctx)->id); 308 *device = dctx->device; 309 PetscFunctionReturn(0); 310 } 311 312 /*@C 313 PetscDeviceContextGetDeviceType - Get the `PetscDeviceType` for a `PetscDeviceContext` 314 315 Not Collective 316 317 Input Parameter: 318 . dctx - The `PetscDeviceContext` 319 320 Output Parameter: 321 . type - The `PetscDeviceType` 322 323 Notes: 324 This routine is a convenience shorthand for `PetscDeviceContextGetDevice()` -> 325 `PetscDeviceGetType()`. 326 327 Level: beginner 328 329 .seealso: `PetscDeviceType`, `PetscDeviceContextGetDevice()`, `PetscDeviceGetType()`, `PetscDevice` 330 @*/ 331 PetscErrorCode PetscDeviceContextGetDeviceType(PetscDeviceContext dctx, PetscDeviceType *type) { 332 PetscDevice device = nullptr; 333 334 PetscFunctionBegin; 335 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 336 PetscValidPointer(type, 2); 337 PetscCall(PetscDeviceContextGetDevice(dctx, &device)); 338 PetscCall(PetscDeviceGetType(device, type)); 339 PetscFunctionReturn(0); 340 } 341 342 /*@C 343 PetscDeviceContextSetUp - Prepares a `PetscDeviceContext` for use 344 345 Not Collective 346 347 Input Parameter: 348 . dctx - The `PetscDeviceContext` 349 350 Developer Notes: 351 This routine is usually the stage where a `PetscDeviceContext` acquires device-side data 352 structures such as streams, events, and (possibly) handles. 353 354 Level: beginner 355 356 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextSetDevice()`, 357 `PetscDeviceContextDestroy()`, `PetscDeviceContextSetFromOptions()` 358 @*/ 359 PetscErrorCode PetscDeviceContextSetUp(PetscDeviceContext dctx) { 360 PetscFunctionBegin; 361 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 362 if (dctx->setup) PetscFunctionReturn(0); 363 if (!dctx->device) { 364 const auto default_dtype = PETSC_DEVICE_DEFAULT(); 365 366 PetscCall(PetscInfo(dctx, "PetscDeviceContext %" PetscInt64_FMT " did not have an explicitly attached PetscDevice, using default with type %s\n", PetscObjectCast(dctx)->id, PetscDeviceTypes[default_dtype])); 367 PetscCall(PetscDeviceContextSetDefaultDeviceForType_Internal(dctx, default_dtype)); 368 } 369 PetscCall(PetscLogEventBegin(DCONTEXT_SetUp, dctx, nullptr, nullptr, nullptr)); 370 PetscUseTypeMethod(dctx, setup); 371 PetscCall(PetscLogEventEnd(DCONTEXT_SetUp, dctx, nullptr, nullptr, nullptr)); 372 dctx->setup = PETSC_TRUE; 373 PetscFunctionReturn(0); 374 } 375 376 static PetscErrorCode PetscDeviceContextDuplicate_Private(PetscDeviceContext dctx, PetscStreamType stype, PetscDeviceContext *dctxdup) { 377 PetscFunctionBegin; 378 PetscCall(PetscLogEventBegin(DCONTEXT_Duplicate, dctx, nullptr, nullptr, nullptr)); 379 PetscCall(PetscDeviceContextCreate(dctxdup)); 380 PetscCall(PetscDeviceContextSetStreamType(*dctxdup, stype)); 381 if (const auto device = dctx->device) PetscCall(PetscDeviceContextSetDevice_Private(*dctxdup, device, dctx->usersetdevice)); 382 PetscCall(PetscDeviceContextSetUp(*dctxdup)); 383 PetscCall(PetscLogEventEnd(DCONTEXT_Duplicate, dctx, nullptr, nullptr, nullptr)); 384 PetscFunctionReturn(0); 385 } 386 387 /*@C 388 PetscDeviceContextDuplicate - Duplicates a `PetscDeviceContext` object 389 390 Not Collective 391 392 Input Parameter: 393 . dctx - The `PetscDeviceContext` to duplicate 394 395 Output Parameter: 396 . dctxdup - The duplicated `PetscDeviceContext` 397 398 Notes: 399 This is a shorthand method for creating a `PetscDeviceContext` with the exact same settings as 400 another. Note however that `dctxdup` does not share any of the underlying data with `dctx`, 401 (including its current stream-state) they are completely separate objects. 402 403 There is no implied ordering between `dctx` or `dctxdup`. 404 405 DAG representation: 406 .vb 407 time -> 408 409 -> dctx - |= CALL =| - dctx ----> 410 - dctxdup -> 411 .ve 412 413 Level: beginner 414 415 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextSetDevice()`, 416 `PetscDeviceContextSetStreamType()` 417 @*/ 418 PetscErrorCode PetscDeviceContextDuplicate(PetscDeviceContext dctx, PetscDeviceContext *dctxdup) { 419 auto stype = PETSC_STREAM_DEFAULT_BLOCKING; 420 421 PetscFunctionBegin; 422 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 423 PetscValidPointer(dctxdup, 2); 424 PetscCall(PetscDeviceContextGetStreamType(dctx, &stype)); 425 PetscCall(PetscDeviceContextDuplicate_Private(dctx, stype, dctxdup)); 426 PetscFunctionReturn(0); 427 } 428 429 /*@C 430 PetscDeviceContextQueryIdle - Returns whether or not a `PetscDeviceContext` is idle 431 432 Not Collective 433 434 Input Parameter: 435 . dctx - The `PetscDeviceContext` 436 437 Output Parameter: 438 . idle - `PETSC_TRUE` if `dctx` has NO work, `PETSC_FALSE` if it has work 439 440 Note: 441 This routine only refers a singular context and does NOT take any of its children into 442 account. That is, if `dctx` is idle but has dependents who do have work this routine still 443 returns `PETSC_TRUE`. 444 445 Level: intermediate 446 447 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextWaitForContext()`, `PetscDeviceContextFork()` 448 @*/ 449 PetscErrorCode PetscDeviceContextQueryIdle(PetscDeviceContext dctx, PetscBool *idle) { 450 PetscFunctionBegin; 451 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 452 PetscValidBoolPointer(idle, 2); 453 PetscCall(PetscLogEventBegin(DCONTEXT_QueryIdle, dctx, nullptr, nullptr, nullptr)); 454 PetscUseTypeMethod(dctx, query, idle); 455 PetscCall(PetscLogEventEnd(DCONTEXT_QueryIdle, dctx, nullptr, nullptr, nullptr)); 456 PetscCall(PetscInfo(dctx, "PetscDeviceContext ('%s', id %" PetscInt64_FMT ") %s idle\n", PetscObjectCast(dctx)->name ? PetscObjectCast(dctx)->name : "unnamed", PetscObjectCast(dctx)->id, *idle ? "was" : "was not")); 457 PetscFunctionReturn(0); 458 } 459 460 /*@C 461 PetscDeviceContextWaitForContext - Make one context wait for another context to finish 462 463 Not Collective 464 465 Input Parameters: 466 + dctxa - The `PetscDeviceContext` object that is waiting 467 - dctxb - The `PetscDeviceContext` object that is being waited on 468 469 Notes: 470 Serializes two `PetscDeviceContext`s. Serialization is performed asynchronously; the host 471 does not wait for the serialization to actually occur. 472 473 This routine uses only the state of `dctxb` at the moment this routine was called, so any 474 future work queued will not affect `dctxa`. It is safe to pass the same context to both 475 arguments (in which case this routine does nothing). 476 477 DAG representation: 478 .vb 479 time -> 480 481 -> dctxa ---/- |= CALL =| - dctxa -> 482 / 483 -> dctxb -/------------------------> 484 .ve 485 486 Level: beginner 487 488 .N ASYNC_API 489 490 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextQueryIdle()`, `PetscDeviceContextJoin()` 491 @*/ 492 PetscErrorCode PetscDeviceContextWaitForContext(PetscDeviceContext dctxa, PetscDeviceContext dctxb) { 493 PetscObject aobj; 494 495 PetscFunctionBegin; 496 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctxa)); 497 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctxb)); 498 PetscCheckCompatibleDeviceContexts(dctxa, 1, dctxb, 2); 499 if (dctxa == dctxb) PetscFunctionReturn(0); 500 aobj = PetscObjectCast(dctxa); 501 PetscCall(PetscLogEventBegin(DCONTEXT_WaitForCtx, dctxa, dctxb, nullptr, nullptr)); 502 PetscUseTypeMethod(dctxa, waitforcontext, dctxb); 503 PetscCallCXX(CxxDataCast(dctxa)->upstream[dctxb] = CxxData::parent_type(dctxb)); 504 PetscCall(PetscLogEventEnd(DCONTEXT_WaitForCtx, dctxa, dctxb, nullptr, nullptr)); 505 PetscCall(PetscInfo(dctxa, "dctx %" PetscInt64_FMT " waiting on dctx %" PetscInt64_FMT "\n", aobj->id, PetscObjectCast(dctxb)->id)); 506 PetscCall(PetscObjectStateIncrease(aobj)); 507 PetscFunctionReturn(0); 508 } 509 510 /*@C 511 PetscDeviceContextForkWithStreamType - Create a set of dependent child contexts from a parent 512 context with a prescribed `PetscStreamType` 513 514 Not Collective, Asynchronous 515 516 Input Parameters: 517 + dctx - The parent `PetscDeviceContext` 518 . stype - The prescribed `PetscStreamType` 519 - n - The number of children to create 520 521 Output Parameter: 522 . dsub - The created child context(s) 523 524 Notes: 525 This routine creates `n` edges of a DAG from a source node which are causally dependent on the 526 source node. This causal dependency is established as-if by calling 527 `PetscDeviceContextWaitForContext()` on every child. 528 529 `dsub` is allocated by this routine and has its lifetime bounded by `dctx`. That is, `dctx` 530 expects to free `dsub` (via `PetscDeviceContextJoin()`) before it itself is destroyed. 531 532 This routine only accounts for work queued on `dctx` up until calling this routine, any 533 subsequent work enqueued on `dctx` has no effect on `dsub`. 534 535 The `PetscStreamType` of `dctx` does not have to equal `stype`. In fact, it is often the case 536 that they are different. This is useful in cases where a routine can locally exploit stream 537 parallelism without needing to worry about what stream type the incoming `PetscDeviceContext` 538 carries. 539 540 DAG representation: 541 .vb 542 time -> 543 544 -> dctx - |= CALL =| -\----> dctx ------> 545 \---> dsub[0] ---> 546 \--> ... -------> 547 \-> dsub[n-1] -> 548 .ve 549 550 Level: intermediate 551 552 .N ASYNC_API 553 554 .seealso: `PetscDeviceContextJoin()`, `PetscDeviceContextSynchronize()`, 555 `PetscDeviceContextQueryIdle()`, `PetscDeviceContextWaitForContext()` 556 @*/ 557 PetscErrorCode PetscDeviceContextForkWithStreamType(PetscDeviceContext dctx, PetscStreamType stype, PetscInt n, PetscDeviceContext **dsub) { 558 // debugging only 559 std::string idList; 560 auto ninput = n; 561 562 PetscFunctionBegin; 563 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 564 PetscAssert(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of contexts requested %" PetscInt_FMT " < 0", n); 565 PetscValidPointer(dsub, 4); 566 *dsub = nullptr; 567 /* reserve 4 chars per id, 2 for number and 2 for ', ' separator */ 568 if (PetscDefined(USE_DEBUG_AND_INFO)) PetscCallCXX(idList.reserve(4 * n)); 569 PetscCall(PetscLogEventBegin(DCONTEXT_Fork, dctx, nullptr, nullptr, nullptr)); 570 /* update child totals */ 571 dctx->numChildren += n; 572 /* now to find out if we have room */ 573 if (dctx->numChildren > dctx->maxNumChildren) { 574 const auto numChildren = dctx->numChildren; 575 auto &maxNumChildren = dctx->maxNumChildren; 576 auto numAllocated = numChildren; 577 578 /* no room, either from having too many kids or not having any */ 579 if (auto &childIDs = dctx->childIDs) { 580 // the difference is backwards because we have not updated maxNumChildren yet 581 numAllocated -= maxNumChildren; 582 /* have existing children, must reallocate them */ 583 PetscCall(PetscRealloc(numChildren * sizeof(*childIDs), &childIDs)); 584 /* clear the extra memory since realloc doesn't do it for us */ 585 PetscCall(PetscArrayzero(std::next(childIDs, maxNumChildren), numAllocated)); 586 } else { 587 /* have no children */ 588 PetscCall(PetscCalloc1(numChildren, &childIDs)); 589 } 590 /* update total number of children */ 591 maxNumChildren = numChildren; 592 PetscCall(PetscLogObjectMemory(PetscObjectCast(dctx), numAllocated * sizeof(*(dctx->childIDs)))); 593 } 594 PetscCall(PetscMalloc1(n, dsub)); 595 PetscCall(PetscLogObjectMemory(PetscObjectCast(dctx), n * sizeof(*dsub))); 596 for (PetscInt i = 0; ninput && (i < dctx->numChildren); ++i) { 597 auto &childID = dctx->childIDs[i]; 598 /* empty child slot */ 599 if (!childID) { 600 auto &childctx = (*dsub)[i]; 601 602 /* create the child context in the image of its parent */ 603 PetscCall(PetscDeviceContextDuplicate_Private(dctx, stype, &childctx)); 604 PetscCall(PetscDeviceContextWaitForContext(childctx, dctx)); 605 /* register the child with its parent */ 606 PetscCall(PetscObjectGetId(PetscObjectCast(childctx), &childID)); 607 if (PetscDefined(USE_DEBUG_AND_INFO)) { 608 PetscCallCXX(idList += std::to_string(childID)); 609 if (ninput != 1) PetscCallCXX(idList += ", "); 610 } 611 --ninput; 612 } 613 } 614 PetscCall(PetscLogEventEnd(DCONTEXT_Fork, dctx, nullptr, nullptr, nullptr)); 615 PetscCall(PetscDebugInfo(dctx, "Forked %" PetscInt_FMT " children from parent %" PetscInt64_FMT " with IDs: %s\n", n, PetscObjectCast(dctx)->id, idList.c_str())); 616 PetscFunctionReturn(0); 617 } 618 619 /*@C 620 PetscDeviceContextFork - Create a set of dependent child contexts from a parent context 621 622 Not Collective, Asynchronous 623 624 Input Parameters: 625 + dctx - The parent `PetscDeviceContext` 626 - n - The number of children to create 627 628 Output Parameter: 629 . dsub - The created child context(s) 630 631 Notes: 632 Behaves identically to `PetscDeviceContextForkWithStreamType()` except that the prescribed 633 `PetscStreamType` is taken from `dctx`. In effect this routine is shorthand for\: 634 635 .vb 636 PetscStreamType stype; 637 638 PetscDeviceContextGetStreamType(dctx, &stype); 639 PetscDeviceContextForkWithStreamType(dctx, stype, ...); 640 .ve 641 642 Level: beginner 643 644 .N ASYNC_API 645 646 .seealso: `PetscDeviceContextForkWithStreamType()`, `PetscDeviceContextJoin()`, 647 `PetscDeviceContextSynchronize()`, `PetscDeviceContextQueryIdle()` 648 @*/ 649 PetscErrorCode PetscDeviceContextFork(PetscDeviceContext dctx, PetscInt n, PetscDeviceContext **dsub) { 650 auto stype = PETSC_STREAM_DEFAULT_BLOCKING; 651 652 PetscFunctionBegin; 653 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 654 PetscCall(PetscDeviceContextGetStreamType(dctx, &stype)); 655 PetscCall(PetscDeviceContextForkWithStreamType(dctx, stype, n, dsub)); 656 PetscFunctionReturn(0); 657 } 658 659 /*@C 660 PetscDeviceContextJoin - Converge a set of child contexts 661 662 Not Collective, Asynchronous 663 664 Input Parameters: 665 + dctx - A `PetscDeviceContext` to converge on 666 . n - The number of sub contexts to converge 667 . joinMode - The type of join to perform 668 - dsub - The sub contexts to converge 669 670 Notes: 671 If `PetscDeviceContextFork()` creates `n` edges from a source node which all depend on the source 672 node, then this routine is the exact mirror. That is, it creates a node (represented in `dctx`) 673 which recieves `n` edges (and optionally destroys them) which is dependent on the completion 674 of all incoming edges. 675 676 If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_DESTROY`. All contexts in `dsub` will be 677 destroyed by this routine. Thus all sub contexts must have been created with the `dctx` 678 passed to this routine. 679 680 If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_SYNC`. All sub contexts will additionally wait on 681 `dctx` after converging. This has the effect of "synchronizing" the outgoing edges. Note the 682 sync suffix does NOT refer to the host, i.e. this routine does NOT call 683 `PetscDeviceSynchronize()`. 684 685 If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_NO_SYNC`. `dctx` waits for all sub contexts but 686 the sub contexts do not wait for one another or `dctx` afterwards. 687 688 DAG representations: 689 If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_DESTROY` 690 .vb 691 time -> 692 693 -> dctx ---------/- |= CALL =| - dctx -> 694 -> dsub[0] -----/ 695 -> ... -------/ 696 -> dsub[n-1] -/ 697 .ve 698 If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_SYNC` 699 .vb 700 time -> 701 702 -> dctx ---------/- |= CALL =| -\----> dctx ------> 703 -> dsub[0] -----/ \---> dsub[0] ---> 704 -> ... -------/ \--> ... -------> 705 -> dsub[n-1] -/ \-> dsub[n-1] -> 706 .ve 707 If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_NO_SYNC` 708 .vb 709 time -> 710 711 -> dctx ----------/- |= CALL =| - dctx -> 712 -> dsub[0] ------/-----------------------> 713 -> ... --------/------------------------> 714 -> dsub[n-1] --/-------------------------> 715 .ve 716 717 Level: beginner 718 719 .N ASYNC_API 720 721 .seealso: `PetscDeviceContextFork()`, `PetscDeviceContextForkWithStreamType()`, 722 `PetscDeviceContextSynchronize()`, `PetscDeviceContextJoinMode` 723 @*/ 724 PetscErrorCode PetscDeviceContextJoin(PetscDeviceContext dctx, PetscInt n, PetscDeviceContextJoinMode joinMode, PetscDeviceContext **dsub) { 725 // debugging only 726 std::string idList; 727 728 PetscFunctionBegin; 729 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 730 /* validity of dctx is checked in the wait-for loop */ 731 PetscValidPointer(dsub, 4); 732 PetscAssert(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of contexts merged %" PetscInt_FMT " < 0", n); 733 /* reserve 4 chars per id, 2 for number and 2 for ', ' separator */ 734 if (PetscDefined(USE_DEBUG_AND_INFO)) PetscCallCXX(idList.reserve(4 * n)); 735 /* first dctx waits on all the incoming edges */ 736 PetscCall(PetscLogEventBegin(DCONTEXT_Join, dctx, nullptr, nullptr, nullptr)); 737 for (PetscInt i = 0; i < n; ++i) { 738 PetscCheckCompatibleDeviceContexts(dctx, 1, (*dsub)[i], 4); 739 PetscCall(PetscDeviceContextWaitForContext(dctx, (*dsub)[i])); 740 if (PetscDefined(USE_DEBUG_AND_INFO)) { 741 PetscCallCXX(idList += std::to_string(PetscObjectCast((*dsub)[i])->id)); 742 if (i + 1 < n) PetscCallCXX(idList += ", "); 743 } 744 } 745 746 /* now we handle the aftermath */ 747 switch (joinMode) { 748 case PETSC_DEVICE_CONTEXT_JOIN_DESTROY: { 749 const auto children = dctx->childIDs; 750 const auto maxchild = dctx->maxNumChildren; 751 auto &nchild = dctx->numChildren; 752 PetscInt j = 0; 753 754 PetscCheck(n <= nchild, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Trying to destroy %" PetscInt_FMT " children of a parent context that only has %" PetscInt_FMT " children, likely trying to restore to wrong parent", n, nchild); 755 /* update child count while it's still fresh in memory */ 756 nchild -= n; 757 for (PetscInt i = 0; i < maxchild; ++i) { 758 if (children[i] && (children[i] == PetscObjectCast((*dsub)[j])->id)) { 759 /* child is one of ours, can destroy it */ 760 PetscCall(PetscDeviceContextDestroy((*dsub) + j)); 761 /* reset the child slot */ 762 children[i] = 0; 763 if (++j == n) break; 764 } 765 } 766 /* gone through the loop but did not find every child */ 767 PetscCheck(j == n, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "%" PetscInt_FMT " contexts still remain after destroy, this may be because you are trying to restore to the wrong parent context, or the device contexts are not in the same order as they were checked out out in", n - j); 768 PetscCall(PetscFree(*dsub)); 769 PetscCall(PetscLogObjectMemory(PetscObjectCast(dctx), -n * sizeof(*dsub))); 770 } break; 771 case PETSC_DEVICE_CONTEXT_JOIN_SYNC: 772 for (PetscInt i = 0; i < n; ++i) PetscCall(PetscDeviceContextWaitForContext((*dsub)[i], dctx)); 773 case PETSC_DEVICE_CONTEXT_JOIN_NO_SYNC: break; 774 default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown PetscDeviceContextJoinMode given"); 775 } 776 PetscCall(PetscLogEventEnd(DCONTEXT_Join, dctx, nullptr, nullptr, nullptr)); 777 778 PetscCall(PetscDebugInfo(dctx, "Joined %" PetscInt_FMT " ctxs to ctx %" PetscInt64_FMT ", mode %s with IDs: %s\n", n, PetscObjectCast(dctx)->id, PetscDeviceContextJoinModes[joinMode], idList.c_str())); 779 PetscFunctionReturn(0); 780 } 781 782 /*@C 783 PetscDeviceContextSynchronize - Block the host until all work queued on a 784 `PetscDeviceContext` has finished 785 786 Not Collective 787 788 Input Parameters: 789 . dctx - The `PetscDeviceContext` to synchronize 790 791 Notes: 792 The host will not return from this routine until `dctx` is idle. Any and all memory 793 operations queued on or otherwise associated with (either explicitly or implicitly via 794 dependencies) are guaranteed to have finished and be globally visible on return. 795 796 In effect, this routine serves as memory and execution barrier. 797 798 DAG representation: 799 .vb 800 time -> 801 802 -> dctx - |= CALL =| - dctx -> 803 .ve 804 805 Level: beginner 806 807 .seealso: `PetscDeviceContextFork()`, `PetscDeviceContextJoin()`, `PetscDeviceContextQueryIdle()` 808 @*/ 809 PetscErrorCode PetscDeviceContextSynchronize(PetscDeviceContext dctx) { 810 PetscFunctionBegin; 811 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 812 PetscCall(PetscLogEventBegin(DCONTEXT_Sync, dctx, nullptr, nullptr, nullptr)); 813 /* if it isn't setup there is nothing to sync on */ 814 if (dctx->setup) { 815 PetscCall((*dctx->ops->synchronize)(dctx)); 816 PetscCall(PetscDeviceContextSyncClearMap_Internal(dctx)); 817 } 818 PetscCall(PetscLogEventEnd(DCONTEXT_Sync, dctx, nullptr, nullptr, nullptr)); 819 PetscFunctionReturn(0); 820 } 821 822 /* every device type has a vector of null PetscDeviceContexts -- one for each device */ 823 static auto nullContexts = std::array<std::vector<PetscDeviceContext>, PETSC_DEVICE_MAX>{}; 824 static auto nullContextsFinalizer = false; 825 826 static PetscErrorCode PetscDeviceContextGetNullContextForDevice_Private(PetscBool user_set_device, PetscDevice device, PetscDeviceContext *dctx) { 827 PetscInt devid; 828 PetscDeviceType dtype; 829 830 PetscFunctionBegin; 831 PetscValidDevice(device, 2); 832 PetscValidPointer(dctx, 3); 833 if (PetscUnlikely(!nullContextsFinalizer)) { 834 const auto finalizer = [] { 835 PetscFunctionBegin; 836 for (auto &&dvec : nullContexts) { 837 for (auto &&dctx : dvec) PetscCall(PetscDeviceContextDestroy(&dctx)); 838 PetscCallCXX(dvec.clear()); 839 } 840 nullContextsFinalizer = false; 841 PetscFunctionReturn(0); 842 }; 843 844 nullContextsFinalizer = true; 845 PetscCall(PetscRegisterFinalize(std::move(finalizer))); 846 } 847 PetscCall(PetscDeviceGetDeviceId(device, &devid)); 848 PetscCall(PetscDeviceGetType(device, &dtype)); 849 { 850 auto &ctxlist = nullContexts[dtype]; 851 852 PetscCheck(devid >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Device ID (%" PetscInt_FMT ") must be positive", devid); 853 // need to resize the container if not big enough because incrementing the iterator in 854 // std::next() (if we haven't initialized that ctx yet) may cause it to fall outside the 855 // current size of the container. 856 if (static_cast<std::size_t>(devid) >= ctxlist.size()) PetscCallCXX(ctxlist.resize(devid + 1)); 857 if (PetscUnlikely(!ctxlist[devid])) { 858 // we have not seen this device before 859 PetscCall(PetscDeviceContextCreate(dctx)); 860 PetscCall(PetscInfo(*dctx, "Initializing null PetscDeviceContext (of type %s) for device %" PetscInt_FMT "\n", PetscDeviceTypes[dtype], devid)); 861 { 862 const auto pobj = PetscObjectCast(*dctx); 863 const auto name = "null context " + std::to_string(devid); 864 const auto prefix = "null_context_" + std::to_string(devid) + '_'; 865 866 PetscCall(PetscObjectSetName(pobj, name.c_str())); 867 PetscCall(PetscObjectSetOptionsPrefix(pobj, prefix.c_str())); 868 } 869 PetscCall(PetscDeviceContextSetStreamType(*dctx, PETSC_STREAM_GLOBAL_BLOCKING)); 870 PetscCall(PetscDeviceContextSetDevice_Private(*dctx, device, user_set_device)); 871 PetscCall(PetscDeviceContextSetUp(*dctx)); 872 // would use ctxlist.cbegin() but GCC 4.8 can't handle const iterator insert! 873 PetscCallCXX(ctxlist.insert(std::next(ctxlist.begin(), devid), *dctx)); 874 } else *dctx = ctxlist[devid]; 875 } 876 PetscFunctionReturn(0); 877 } 878 879 /* 880 Gets the "NULL" context for the current PetscDeviceType and PetscDevice. NULL contexts are 881 guaranteed to always be globally blocking. 882 */ 883 PetscErrorCode PetscDeviceContextGetNullContext_Internal(PetscDeviceContext *dctx) { 884 PetscDeviceContext gctx; 885 PetscDevice gdev = nullptr; 886 887 PetscFunctionBegin; 888 PetscValidPointer(dctx, 1); 889 PetscCall(PetscDeviceContextGetCurrentContext(&gctx)); 890 PetscCall(PetscDeviceContextGetDevice(gctx, &gdev)); 891 PetscCall(PetscDeviceContextGetNullContextForDevice_Private(gctx->usersetdevice, gdev, dctx)); 892 PetscFunctionReturn(0); 893 } 894 895 /*@C 896 PetscDeviceContextSetFromOptions - Configure a `PetscDeviceContext` from the options database 897 898 Collective on `comm` or `dctx` 899 900 Input Parameters: 901 + comm - MPI communicator on which to query the options database (optional) 902 - dctx - The `PetscDeviceContext` to configure 903 904 Output Parameter: 905 . dctx - The `PetscDeviceContext` 906 907 Options Database: 908 + -device_context_stream_type - type of stream to create inside the `PetscDeviceContext` - 909 `PetscDeviceContextSetStreamType()` 910 - -device_context_device_type - the type of `PetscDevice` to attach by default - `PetscDeviceType` 911 912 Notes: 913 The user may pass `MPI_COMM_NULL` for `comm` in which case the communicator of `dctx` is 914 used (which is always `PETSC_COMM_SELF`). 915 916 Level: beginner 917 918 .seealso: `PetscDeviceContextSetStreamType()`, `PetscDeviceContextSetDevice()`, 919 `PetscDeviceContextView()` 920 @*/ 921 PetscErrorCode PetscDeviceContextSetFromOptions(MPI_Comm comm, PetscDeviceContext dctx) { 922 const auto pobj = PetscObjectCast(dctx); 923 auto dtype = std::make_pair(PETSC_DEVICE_DEFAULT(), PETSC_FALSE); 924 auto stype = std::make_pair(PETSC_DEVICE_CONTEXT_DEFAULT_STREAM_TYPE, PETSC_FALSE); 925 auto old_comm = PETSC_COMM_SELF; 926 927 PetscFunctionBegin; 928 // do not user getoptionalnullcontext here, the user is not allowed to set it from options! 929 PetscValidDeviceContext(dctx, 2); 930 /* set the device type first */ 931 if (const auto device = dctx->device) PetscCall(PetscDeviceGetType(device, &dtype.first)); 932 PetscCall(PetscDeviceContextGetStreamType(dctx, &stype.first)); 933 934 if (comm == MPI_COMM_NULL) { 935 PetscCall(PetscObjectGetComm(pobj, &comm)); 936 } else { 937 // briefly set the communicator for dctx (it is always PETSC_COMM_SELF) so 938 // PetscObjectOptionsBegin() behaves as if dctx had comm 939 old_comm = Petsc::util::exchange(pobj->comm, comm); 940 } 941 942 PetscObjectOptionsBegin(pobj); 943 PetscCall(PetscDeviceContextQueryOptions_Internal(PetscOptionsObject, dtype, stype)); 944 PetscOptionsEnd(); 945 // reset the comm (should be PETSC_COMM_SELF) 946 if (comm != MPI_COMM_NULL) pobj->comm = old_comm; 947 if (dtype.second) PetscCall(PetscDeviceContextSetDefaultDeviceForType_Internal(dctx, dtype.first)); 948 if (stype.second) PetscCall(PetscDeviceContextSetStreamType(dctx, stype.first)); 949 PetscCall(PetscDeviceContextSetUp(dctx)); 950 PetscFunctionReturn(0); 951 } 952 953 /*@C 954 PetscDeviceContextView - View a `PetscDeviceContext` 955 956 Collective on `viewer` 957 958 Input Parameters: 959 + dctx - The `PetscDeviceContext` 960 - viewer - The `PetscViewer` to view `dctx` with (may be `NULL`) 961 962 Notes: 963 If `viewer` is `NULL`, `PETSC_VIEWER_STDOUT_WORLD` is used instead, in which case this 964 routine is collective on `PETSC_COMM_WORLD`. 965 966 Level: beginner 967 968 .seealso: `PetscDeviceContextViewFromOptions()`, `PetscDeviceView()`, `PETSC_VIEWER_STDOUT_WORLD`, `PetscDeviceContextCreate()` 969 @*/ 970 PetscErrorCode PetscDeviceContextView(PetscDeviceContext dctx, PetscViewer viewer) { 971 PetscBool iascii; 972 973 PetscFunctionBegin; 974 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 975 if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &viewer)); 976 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2); 977 PetscCall(PetscObjectTypeCompare(PetscObjectCast(viewer), PETSCVIEWERASCII, &iascii)); 978 if (iascii) { 979 auto stype = PETSC_STREAM_DEFAULT_BLOCKING; 980 PetscViewer sub; 981 982 PetscCall(PetscViewerGetSubViewer(viewer, PETSC_COMM_SELF, &sub)); 983 PetscCall(PetscObjectPrintClassNamePrefixType(PetscObjectCast(dctx), sub)); 984 PetscCall(PetscViewerASCIIPushTab(sub)); 985 PetscCall(PetscDeviceContextGetStreamType(dctx, &stype)); 986 PetscCall(PetscViewerASCIIPrintf(sub, "stream type: %s\n", PetscStreamTypes[stype])); 987 PetscCall(PetscViewerASCIIPrintf(sub, "children: %" PetscInt_FMT "\n", dctx->numChildren)); 988 if (const auto nchild = dctx->numChildren) { 989 PetscCall(PetscViewerASCIIPushTab(sub)); 990 for (PetscInt i = 0; i < nchild; ++i) { 991 if (i == nchild - 1) { 992 PetscCall(PetscViewerASCIIPrintf(sub, "%" PetscInt64_FMT, dctx->childIDs[i])); 993 } else { 994 PetscCall(PetscViewerASCIIPrintf(sub, "%" PetscInt64_FMT ", ", dctx->childIDs[i])); 995 } 996 } 997 } 998 PetscCall(PetscViewerASCIIPopTab(sub)); 999 PetscCall(PetscViewerRestoreSubViewer(viewer, PETSC_COMM_SELF, &sub)); 1000 PetscCall(PetscViewerFlush(viewer)); 1001 PetscCall(PetscViewerASCIIPushTab(viewer)); 1002 } 1003 if (const auto device = dctx->device) PetscCall(PetscDeviceView(device, viewer)); 1004 if (iascii) PetscCall(PetscViewerASCIIPopTab(viewer)); 1005 PetscFunctionReturn(0); 1006 } 1007 1008 /*@C 1009 PetscDeviceContextViewFromOptions - View a `PetscDeviceContext` from options 1010 1011 Input Parameters: 1012 + dctx - The `PetscDeviceContext` to view 1013 . obj - Optional `PetscObject` to associate (may be `NULL`) 1014 - name - The command line option 1015 1016 Level: beginner 1017 1018 .seealso: `PetscDeviceContextView()`, `PetscObjectViewFromOptions()`, `PetscDeviceContextCreate()` 1019 @*/ 1020 PetscErrorCode PetscDeviceContextViewFromOptions(PetscDeviceContext dctx, PetscObject obj, const char name[]) { 1021 PetscFunctionBegin; 1022 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 1023 if (obj) PetscValidHeader(obj, 2); 1024 PetscValidCharPointer(name, 3); 1025 PetscCall(PetscObjectViewFromOptions(PetscObjectCast(dctx), obj, name)); 1026 PetscFunctionReturn(0); 1027 } 1028