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