1 #include "petscdevice_interface_internal.hpp" /*I <petscdevice.h> I*/ 2 #include <petsc/private/petscadvancedmacros.h> 3 4 #include <petsc/private/cpp/register_finalize.hpp> 5 6 #include "../impls/host/hostdevice.hpp" 7 #if PetscDefined(HAVE_CUPM) 8 #include "../impls/cupm/cupmdevice.hpp" 9 #endif 10 #if PetscDefined(HAVE_SYCL) 11 #include "../impls/sycl/sycldevice.hpp" 12 #endif 13 14 #include <utility> // std::make_pair 15 16 using namespace Petsc::device; 17 18 #if defined(PETSC_HAVE_CUPM) 19 int PetscDeviceCUPMRuntimeArch = 0; 20 #endif 21 22 namespace 23 { 24 25 /* 26 note to anyone adding more classes, the name must be ALL_CAPS_SHORT_NAME + Device exactly to 27 be picked up by the switch-case macros below 28 */ 29 host::Device HOSTDevice{PetscDeviceContextCreate_HOST}; 30 #if PetscDefined(HAVE_CUDA) 31 cupm::Device<cupm::DeviceType::CUDA> CUDADevice{PetscDeviceContextCreate_CUDA}; 32 #endif 33 #if PetscDefined(HAVE_HIP) 34 cupm::Device<cupm::DeviceType::HIP> HIPDevice{PetscDeviceContextCreate_HIP}; 35 #endif 36 #if PetscDefined(HAVE_SYCL) 37 sycl::Device SYCLDevice{PetscDeviceContextCreate_SYCL}; 38 #endif 39 40 } // namespace 41 42 #define PETSC_DEVICE_CASE(IMPLS, func, ...) \ 43 case PetscConcat_(PETSC_DEVICE_, IMPLS): { \ 44 PetscCall(PetscConcat_(IMPLS, Device).func(__VA_ARGS__)); \ 45 } break 46 47 #define PETSC_VOID_0(...) ((void)0) 48 49 /* 50 Suppose you have: 51 52 CUDADevice.myFunction(arg1,arg2) 53 54 that you would like to conditionally define and call in a switch-case: 55 56 switch(PetscDeviceType) { 57 #if PetscDefined(HAVE_CUDA) 58 case PETSC_DEVICE_CUDA: { 59 PetscCall(CUDADevice.myFunction(arg1,arg2)); 60 } break; 61 #endif 62 } 63 64 then calling this macro: 65 66 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA,myFunction,arg1,arg2) 67 68 will expand to the following case statement: 69 70 case PETSC_DEVICE_CUDA: { 71 PetscCall(CUDADevice.myFunction(arg1,arg2)); 72 } break 73 74 if PetscDefined(HAVE_CUDA) evaluates to 1, and expand to nothing otherwise 75 */ 76 #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED(IMPLS, func, ...) PetscIfPetscDefined(PetscConcat_(HAVE_, IMPLS), PETSC_DEVICE_CASE, PETSC_VOID_0)(IMPLS, func, __VA_ARGS__) 77 78 /*@C 79 PetscDeviceCreate - Get a new handle for a particular device (often a GPU) type 80 81 Not Collective 82 83 Input Parameters: 84 + type - The type of `PetscDevice` 85 - devid - The numeric ID# of the device (pass `PETSC_DECIDE` to assign automatically) 86 87 Output Parameter: 88 . device - The `PetscDevice` 89 90 Level: beginner 91 92 Notes: 93 This routine may initialize `PetscDevice`. If this is the case, it may cause some sort of 94 device synchronization. 95 96 `devid` is what you might pass to `cudaSetDevice()` for example. 97 98 .seealso: `PetscDevice`, `PetscDeviceInitType`, 99 `PetscDeviceInitialize()`, `PetscDeviceInitialized()`, `PetscDeviceConfigure()`, 100 `PetscDeviceView()`, `PetscDeviceDestroy()` 101 @*/ 102 PetscErrorCode PetscDeviceCreate(PetscDeviceType type, PetscInt devid, PetscDevice *device) 103 { 104 static PetscInt PetscDeviceCounter = 0; 105 106 PetscFunctionBegin; 107 PetscValidDeviceType(type, 1); 108 PetscAssertPointer(device, 3); 109 PetscCall(PetscDeviceInitializePackage()); 110 111 PetscCall(PetscNew(device)); 112 (*device)->id = PetscDeviceCounter++; 113 (*device)->type = type; 114 (*device)->refcnt = 1; 115 /* 116 if you are adding a device, you also need to add its initialization in 117 PetscDeviceInitializeTypeFromOptions_Private() below 118 */ 119 switch (type) { 120 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HOST, getDevice, *device, devid); 121 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA, getDevice, *device, devid); 122 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP, getDevice, *device, devid); 123 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(SYCL, getDevice, *device, devid); 124 default: 125 /* in case the above macros expand to nothing this silences any unused variable warnings */ 126 (void)devid; 127 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "PETSc was seemingly configured for PetscDeviceType %s but we've fallen through all cases in a switch", PetscDeviceTypes[type]); 128 } 129 PetscFunctionReturn(PETSC_SUCCESS); 130 } 131 132 /*@C 133 PetscDeviceDestroy - Free a `PetscDevice` 134 135 Not Collective 136 137 Input Parameter: 138 . device - The `PetscDevice` 139 140 Level: beginner 141 142 .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceView()`, 143 `PetscDeviceGetType()`, `PetscDeviceGetDeviceId()` 144 @*/ 145 PetscErrorCode PetscDeviceDestroy(PetscDevice *device) 146 { 147 PetscFunctionBegin; 148 PetscAssertPointer(device, 1); 149 if (!*device) PetscFunctionReturn(PETSC_SUCCESS); 150 PetscValidDevice(*device, 1); 151 PetscCall(PetscDeviceDereference_Internal(*device)); 152 if ((*device)->refcnt) { 153 *device = nullptr; 154 PetscFunctionReturn(PETSC_SUCCESS); 155 } 156 PetscCall(PetscFree((*device)->data)); 157 PetscCall(PetscFree(*device)); 158 PetscFunctionReturn(PETSC_SUCCESS); 159 } 160 161 /*@C 162 PetscDeviceConfigure - Configure a particular `PetscDevice` 163 164 Not Collective 165 166 Input Parameter: 167 . device - The `PetscDevice` to configure 168 169 Level: beginner 170 171 Notes: 172 The user should not assume that this is a cheap operation. 173 174 .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceView()`, `PetscDeviceDestroy()`, 175 `PetscDeviceGetType()`, `PetscDeviceGetDeviceId()` 176 @*/ 177 PetscErrorCode PetscDeviceConfigure(PetscDevice device) 178 { 179 PetscFunctionBegin; 180 PetscValidDevice(device, 1); 181 /* 182 if no available configuration is available, this cascades all the way down to default 183 and error 184 */ 185 switch (const auto dtype = device->type) { 186 case PETSC_DEVICE_HOST: 187 if (PetscDefined(HAVE_HOST)) break; // always true 188 case PETSC_DEVICE_CUDA: 189 if (PetscDefined(HAVE_CUDA)) break; 190 goto error; 191 case PETSC_DEVICE_HIP: 192 if (PetscDefined(HAVE_HIP)) break; 193 goto error; 194 case PETSC_DEVICE_SYCL: 195 if (PetscDefined(HAVE_SYCL)) break; 196 goto error; 197 default: 198 error: 199 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "PETSc was not configured for PetscDeviceType %s", PetscDeviceTypes[dtype]); 200 } 201 PetscUseTypeMethod(device, configure); 202 PetscFunctionReturn(PETSC_SUCCESS); 203 } 204 205 /*@ 206 PetscDeviceView - View a `PetscDevice` 207 208 Collective on viewer 209 210 Input Parameters: 211 + device - The `PetscDevice` to view 212 - viewer - The `PetscViewer` to view the device with (`NULL` for `PETSC_VIEWER_STDOUT_WORLD`) 213 214 Level: beginner 215 216 .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceConfigure()`, 217 `PetscDeviceDestroy()`, `PetscDeviceGetType()`, `PetscDeviceGetDeviceId()` 218 @*/ 219 PetscErrorCode PetscDeviceView(PetscDevice device, PetscViewer viewer) 220 { 221 auto sub = viewer; 222 PetscBool isascii; 223 224 PetscFunctionBegin; 225 PetscValidDevice(device, 1); 226 if (viewer) { 227 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2); 228 PetscCall(PetscObjectTypeCompare(PetscObjectCast(viewer), PETSCVIEWERASCII, &isascii)); 229 } else { 230 PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &viewer)); 231 isascii = PETSC_TRUE; 232 } 233 234 if (isascii) { 235 auto dtype = PETSC_DEVICE_HOST; 236 MPI_Comm comm; 237 PetscMPIInt size; 238 PetscInt id = 0; 239 240 PetscCall(PetscObjectGetComm(PetscObjectCast(viewer), &comm)); 241 PetscCallMPI(MPI_Comm_size(comm, &size)); 242 243 PetscCall(PetscDeviceGetDeviceId(device, &id)); 244 PetscCall(PetscDeviceGetType(device, &dtype)); 245 PetscCall(PetscViewerGetSubViewer(viewer, PETSC_COMM_SELF, &sub)); 246 PetscCall(PetscViewerASCIIPrintf(sub, "PetscDevice Object: %d MPI %s\n", size, size == 1 ? "process" : "processes")); 247 PetscCall(PetscViewerASCIIPushTab(sub)); 248 PetscCall(PetscViewerASCIIPrintf(sub, "type: %s\n", PetscDeviceTypes[dtype])); 249 PetscCall(PetscViewerASCIIPrintf(sub, "id: %" PetscInt_FMT "\n", id)); 250 } 251 252 // see if impls has extra viewer stuff 253 PetscTryTypeMethod(device, view, sub); 254 255 if (isascii) { 256 // undo the ASCII specific stuff 257 PetscCall(PetscViewerASCIIPopTab(sub)); 258 PetscCall(PetscViewerRestoreSubViewer(viewer, PETSC_COMM_SELF, &sub)); 259 } 260 PetscFunctionReturn(PETSC_SUCCESS); 261 } 262 263 /*@ 264 PetscDeviceGetType - Get the type of device 265 266 Not Collective 267 268 Input Parameter: 269 . device - The `PetscDevice` 270 271 Output Parameter: 272 . type - The `PetscDeviceType` 273 274 Level: beginner 275 276 .seealso: `PetscDevice`, `PetscDeviceType`, `PetscDeviceSetDefaultDeviceType()`, 277 `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceDestroy()`, 278 `PetscDeviceGetDeviceId()`, `PETSC_DEVICE_DEFAULT()` 279 @*/ 280 PetscErrorCode PetscDeviceGetType(PetscDevice device, PetscDeviceType *type) 281 { 282 PetscFunctionBegin; 283 PetscValidDevice(device, 1); 284 PetscAssertPointer(type, 2); 285 *type = device->type; 286 PetscFunctionReturn(PETSC_SUCCESS); 287 } 288 289 /*@C 290 PetscDeviceGetDeviceId - Get the device ID for a `PetscDevice` 291 292 Not Collective 293 294 Input Parameter: 295 . device - The `PetscDevice` 296 297 Output Parameter: 298 . id - The id 299 300 Level: beginner 301 302 Notes: 303 The returned ID may have been assigned by the underlying device backend. For example if the 304 backend is CUDA then `id` is exactly the value returned by `cudaGetDevice()` at the time when 305 this device was configured. 306 307 .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceGetType()` 308 @*/ 309 PetscErrorCode PetscDeviceGetDeviceId(PetscDevice device, PetscInt *id) 310 { 311 PetscFunctionBegin; 312 PetscValidDevice(device, 1); 313 PetscAssertPointer(id, 2); 314 *id = device->deviceId; 315 PetscFunctionReturn(PETSC_SUCCESS); 316 } 317 318 namespace 319 { 320 321 struct DefaultDeviceType : public Petsc::RegisterFinalizeable<DefaultDeviceType> { 322 PetscDeviceType type = PETSC_DEVICE_HARDWARE_DEFAULT_TYPE; 323 324 PetscErrorCode finalize_() noexcept 325 { 326 PetscFunctionBegin; 327 type = PETSC_DEVICE_HARDWARE_DEFAULT_TYPE; 328 PetscFunctionReturn(PETSC_SUCCESS); 329 } 330 }; 331 332 auto default_device_type = DefaultDeviceType(); 333 334 } // namespace 335 336 /*@C 337 PETSC_DEVICE_DEFAULT - Retrieve the current default `PetscDeviceType` 338 339 Not Collective 340 341 Level: beginner 342 343 Notes: 344 Unless selected by the user, the default device is selected in the following order\: 345 `PETSC_DEVICE_HIP`, `PETSC_DEVICE_CUDA`, `PETSC_DEVICE_SYCL`, `PETSC_DEVICE_HOST`. 346 347 .seealso: `PetscDeviceType`, `PetscDeviceSetDefaultDeviceType()`, `PetscDeviceGetType()` 348 @*/ 349 PetscDeviceType PETSC_DEVICE_DEFAULT(void) 350 { 351 return default_device_type.type; 352 } 353 354 /*@C 355 PetscDeviceSetDefaultDeviceType - Set the default device type for `PetscDevice` 356 357 Not Collective 358 359 Input Parameter: 360 . type - the new default device type 361 362 Level: beginner 363 364 Notes: 365 This sets the `PetscDeviceType` returned by `PETSC_DEVICE_DEFAULT()`. 366 367 .seealso: `PetscDeviceType`, `PetscDeviceGetType`, 368 @*/ 369 PetscErrorCode PetscDeviceSetDefaultDeviceType(PetscDeviceType type) 370 { 371 PetscFunctionBegin; 372 PetscValidDeviceType(type, 1); 373 if (default_device_type.type != type) { 374 // no need to waster a PetscRegisterFinalize() slot if we don't change it 375 default_device_type.type = type; 376 PetscCall(default_device_type.register_finalize()); 377 } 378 PetscFunctionReturn(PETSC_SUCCESS); 379 } 380 381 namespace 382 { 383 384 std::array<std::pair<PetscDevice, bool>, PETSC_DEVICE_MAX> defaultDevices = {}; 385 386 /* 387 Actual initialization function; any functions claiming to initialize PetscDevice or 388 PetscDeviceContext will have to run through this one 389 */ 390 PetscErrorCode PetscDeviceInitializeDefaultDevice_Internal(PetscDeviceType type, PetscInt defaultDeviceId) 391 { 392 PetscFunctionBegin; 393 PetscValidDeviceType(type, 1); 394 if (PetscUnlikely(!PetscDeviceInitialized(type))) { 395 auto &dev = defaultDevices[type].first; 396 auto &init = defaultDevices[type].second; 397 398 PetscAssert(!dev, PETSC_COMM_SELF, PETSC_ERR_MEM, "Trying to overwrite existing default device of type %s", PetscDeviceTypes[type]); 399 PetscCall(PetscDeviceCreate(type, defaultDeviceId, &dev)); 400 PetscCall(PetscDeviceConfigure(dev)); 401 init = true; 402 } 403 PetscFunctionReturn(PETSC_SUCCESS); 404 } 405 406 } // namespace 407 408 /*@C 409 PetscDeviceInitialize - Initialize `PetscDevice` 410 411 Not Collective 412 413 Input Parameter: 414 . type - The `PetscDeviceType` to initialize 415 416 Level: beginner 417 418 Notes: 419 Eagerly initializes the corresponding `PetscDeviceType` if needed. If this is the case it may 420 result in device synchronization. 421 422 .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceInitialized()`, 423 `PetscDeviceCreate()`, `PetscDeviceDestroy()` 424 @*/ 425 PetscErrorCode PetscDeviceInitialize(PetscDeviceType type) 426 { 427 PetscFunctionBegin; 428 PetscValidDeviceType(type, 1); 429 PetscCall(PetscDeviceInitializeDefaultDevice_Internal(type, PETSC_DECIDE)); 430 PetscFunctionReturn(PETSC_SUCCESS); 431 } 432 433 /*@C 434 PetscDeviceInitialized - Determines whether `PetscDevice` is initialized for a particular 435 `PetscDeviceType` 436 437 Not Collective 438 439 Input Parameter: 440 . type - The `PetscDeviceType` to check 441 442 Level: beginner 443 444 Notes: 445 Returns `PETSC_TRUE` if `type` is initialized, `PETSC_FALSE` otherwise. 446 447 If one has not configured PETSc for a particular `PetscDeviceType` then this routine will 448 return `PETSC_FALSE` for that `PetscDeviceType`. 449 450 .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceInitialize()`, 451 `PetscDeviceCreate()`, `PetscDeviceDestroy()` 452 @*/ 453 PetscBool PetscDeviceInitialized(PetscDeviceType type) 454 { 455 return static_cast<PetscBool>(PetscDeviceConfiguredFor_Internal(type) && defaultDevices[type].second); 456 } 457 458 /* Get the default PetscDevice for a particular type and constructs them if lazily initialized. */ 459 PetscErrorCode PetscDeviceGetDefaultForType_Internal(PetscDeviceType type, PetscDevice *device) 460 { 461 PetscFunctionBegin; 462 PetscAssertPointer(device, 2); 463 PetscCall(PetscDeviceInitialize(type)); 464 *device = defaultDevices[type].first; 465 PetscFunctionReturn(PETSC_SUCCESS); 466 } 467 468 /*@C 469 PetscDeviceGetAttribute - Query a particular attribute of a `PetscDevice` 470 471 Not Collective 472 473 Input Parameters: 474 + device - The `PetscDevice` 475 - attr - The attribute 476 477 Output Parameter: 478 . value - The value of the attribute 479 480 Level: intermediate 481 482 Notes: 483 Since different attributes are often different types `value` is a `void *` to accommodate 484 them all. The underlying type of the attribute is therefore included in the name of the 485 `PetscDeviceAttribute` responsible for querying it. For example, 486 `PETSC_DEVICE_ATTR_SIZE_T_SHARED_MEM_PER_BLOCK` is of type `size_t`. 487 488 .seealso: `PetscDeviceAtrtibute`, `PetscDeviceConfigure()`, `PetscDevice` 489 @*/ 490 PetscErrorCode PetscDeviceGetAttribute(PetscDevice device, PetscDeviceAttribute attr, void *value) 491 { 492 PetscFunctionBegin; 493 PetscValidDevice(device, 1); 494 PetscValidDeviceAttribute(attr, 2); 495 PetscAssertPointer(value, 3); 496 PetscUseTypeMethod(device, getattribute, attr, value); 497 PetscFunctionReturn(PETSC_SUCCESS); 498 } 499 500 namespace 501 { 502 503 PetscErrorCode PetscDeviceInitializeTypeFromOptions_Private(MPI_Comm comm, PetscDeviceType type, PetscInt defaultDeviceId, PetscBool defaultView, PetscDeviceInitType *defaultInitType) 504 { 505 PetscFunctionBegin; 506 if (!PetscDeviceConfiguredFor_Internal(type)) { 507 PetscCall(PetscInfo(nullptr, "PetscDeviceType %s not available\n", PetscDeviceTypes[type])); 508 defaultDevices[type].first = nullptr; 509 PetscFunctionReturn(PETSC_SUCCESS); 510 } 511 PetscCall(PetscInfo(nullptr, "PetscDeviceType %s available, initializing\n", PetscDeviceTypes[type])); 512 /* ugly switch needed to pick the right global variable... could maybe do this as a union? */ 513 switch (type) { 514 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HOST, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType); 515 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType); 516 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType); 517 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(SYCL, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType); 518 default: 519 SETERRQ(comm, PETSC_ERR_PLIB, "PETSc was seemingly configured for PetscDeviceType %s but we've fallen through all cases in a switch", PetscDeviceTypes[type]); 520 } 521 PetscCall(PetscInfo(nullptr, "PetscDevice %s initialized, default device id %" PetscInt_FMT ", view %s, init type %s\n", PetscDeviceTypes[type], defaultDeviceId, PetscBools[defaultView], PetscDeviceInitTypes[Petsc::util::to_underlying(*defaultInitType)])); 522 /* 523 defaultInitType, defaultView and defaultDeviceId now represent what the individual TYPES 524 have decided to initialize as 525 */ 526 if ((*defaultInitType == PETSC_DEVICE_INIT_EAGER) || defaultView) { 527 PetscCall(PetscInfo(nullptr, "Eagerly initializing %s PetscDevice\n", PetscDeviceTypes[type])); 528 PetscCall(PetscDeviceInitializeDefaultDevice_Internal(type, defaultDeviceId)); 529 if (defaultView) PetscCall(PetscDeviceView(defaultDevices[type].first, nullptr)); 530 } 531 PetscFunctionReturn(PETSC_SUCCESS); 532 } 533 534 PetscErrorCode PetscDeviceInitializeQueryOptions_Private(MPI_Comm comm, PetscDeviceType *deviceContextInitDevice, PetscDeviceInitType *defaultInitType, PetscInt *defaultDeviceId, PetscBool *defaultDeviceIdSet, PetscBool *defaultView) 535 { 536 PetscInt initIdx = PETSC_DEVICE_INIT_LAZY; 537 auto initDeviceIdx = static_cast<PetscInt>(*deviceContextInitDevice); 538 auto flg = PETSC_FALSE; 539 540 PetscFunctionBegin; 541 PetscCall(PetscOptionsHasName(nullptr, nullptr, "-log_view_gpu_time", &flg)); 542 if (flg) PetscCall(PetscLogGpuTime()); 543 544 PetscOptionsBegin(comm, nullptr, "PetscDevice Options", "Sys"); 545 PetscCall(PetscOptionsEList("-device_enable", "How (or whether) to initialize PetscDevices", "PetscDeviceInitialize()", PetscDeviceInitTypes, 3, PetscDeviceInitTypes[initIdx], &initIdx, nullptr)); 546 PetscCall(PetscOptionsEList("-default_device_type", "Set the PetscDeviceType returned by PETSC_DEVICE_DEFAULT()", "PetscDeviceSetDefaultDeviceType()", PetscDeviceTypes, PETSC_DEVICE_MAX, PetscDeviceTypes[initDeviceIdx], &initDeviceIdx, defaultDeviceIdSet)); 547 PetscCall(PetscOptionsRangeInt("-device_select", "Which device to use. Pass " PetscStringize(PETSC_DECIDE) " to have PETSc decide or (given they exist) [0-" PetscStringize(PETSC_DEVICE_MAX_DEVICES) ") for a specific device", "PetscDeviceCreate()", *defaultDeviceId, defaultDeviceId, nullptr, PETSC_DECIDE, PETSC_DEVICE_MAX_DEVICES)); 548 PetscCall(PetscOptionsBool("-device_view", "Display device information and assignments (forces eager initialization)", "PetscDeviceView()", *defaultView, defaultView, &flg)); 549 PetscOptionsEnd(); 550 551 if (initIdx == PETSC_DEVICE_INIT_NONE) { 552 /* disabled all device initialization if devices are globally disabled */ 553 PetscCheck(*defaultDeviceId == PETSC_DECIDE, comm, PETSC_ERR_USER_INPUT, "You have disabled devices but also specified a particular device to use, these options are mutually exclusive"); 554 *defaultView = PETSC_FALSE; 555 initDeviceIdx = PETSC_DEVICE_HOST; 556 } else { 557 *defaultView = static_cast<PetscBool>(*defaultView && flg); 558 if (*defaultView) initIdx = PETSC_DEVICE_INIT_EAGER; 559 } 560 *defaultInitType = PetscDeviceInitTypeCast(initIdx); 561 *deviceContextInitDevice = PetscDeviceTypeCast(initDeviceIdx); 562 PetscFunctionReturn(PETSC_SUCCESS); 563 } 564 565 /* called from PetscFinalize() do not call yourself! */ 566 PetscErrorCode PetscDeviceFinalize_Private() 567 { 568 PetscFunctionBegin; 569 if (PetscDefined(USE_DEBUG)) { 570 /* 571 you might be thinking, why on earth are you registered yet another finalizer in a 572 function already called during PetscRegisterFinalizeAll()? If this seems stupid it's 573 because it is. 574 575 The crux of the problem is that the initializer (and therefore the ~finalizer~) of 576 PetscDeviceContext is guaranteed to run after PetscDevice's. So if the global context had 577 a default PetscDevice attached, that PetscDevice will have a reference count >0 and hence 578 won't be destroyed yet. So we need to repeat the check that all devices have been 579 destroyed again ~after~ the global context is destroyed. In summary: 580 581 1. This finalizer runs and destroys all devices, except it may not because the global 582 context may still hold a reference! 583 2. The global context finalizer runs and does the final reference count decrement 584 required, which actually destroys the held device. 585 3. Our newly added finalizer runs and checks that all is well. 586 */ 587 PetscCall(PetscRegisterFinalize([] { 588 PetscFunctionBegin; 589 for (auto &&device : defaultDevices) { 590 const auto dev = device.first; 591 592 PetscCheck(!dev, PETSC_COMM_WORLD, PETSC_ERR_COR, "Device of type '%s' had reference count %" PetscInt_FMT " and was not fully destroyed during PetscFinalize()", PetscDeviceTypes[dev->type], dev->refcnt); 593 } 594 PetscFunctionReturn(PETSC_SUCCESS); 595 })); 596 } 597 for (auto &&device : defaultDevices) { 598 PetscCall(PetscDeviceDestroy(&device.first)); 599 device.second = false; 600 } 601 PetscFunctionReturn(PETSC_SUCCESS); 602 } 603 604 } // namespace 605 606 /* 607 Begins the init proceeedings for the entire PetscDevice stack. there are 3 stages of 608 initialization types: 609 610 1. defaultInitType - how does PetscDevice as a whole expect to initialize? 611 2. subTypeDefaultInitType - how does each PetscDevice implementation expect to initialize? 612 e.g. you may want to blanket disable PetscDevice init (and disable say Kokkos init), but 613 have all CUDA devices still initialize. 614 615 All told the following happens: 616 617 0. defaultInitType -> LAZY 618 1. Check for log_view/log_summary, if yes defaultInitType -> EAGER 619 2. PetscDevice initializes each sub type with deviceDefaultInitType. 620 2.1 Each enabled PetscDevice sub-type then does the above disable or view check in addition 621 to checking for specific device init. if view or specific device init 622 subTypeDefaultInitType -> EAGER. disabled once again overrides all. 623 */ 624 625 PetscErrorCode PetscDeviceInitializeFromOptions_Internal(MPI_Comm comm) 626 { 627 auto defaultView = PETSC_FALSE; 628 auto initializeDeviceContextEagerly = PETSC_FALSE; 629 auto defaultDeviceIdSet = PETSC_FALSE; 630 auto defaultDeviceId = PetscInt{PETSC_DECIDE}; 631 auto deviceContextInitDevice = PETSC_DEVICE_DEFAULT(); 632 auto defaultInitType = PETSC_DEVICE_INIT_LAZY; 633 634 PetscFunctionBegin; 635 if (PetscDefined(USE_DEBUG)) { 636 int result; 637 638 PetscCallMPI(MPI_Comm_compare(comm, PETSC_COMM_WORLD, &result)); 639 /* in order to accurately assign ranks to gpus we need to get the MPI_Comm_rank of the 640 * global space */ 641 if (PetscUnlikely(result != MPI_IDENT)) { 642 char name[MPI_MAX_OBJECT_NAME] = {}; 643 int len; /* unused */ 644 645 PetscCallMPI(MPI_Comm_get_name(comm, name, &len)); 646 SETERRQ(comm, PETSC_ERR_MPI, "Default devices being initialized on MPI_Comm '%s' not PETSC_COMM_WORLD", name); 647 } 648 } 649 comm = PETSC_COMM_WORLD; /* from this point on we assume we're on PETSC_COMM_WORLD */ 650 PetscCall(PetscRegisterFinalize(PetscDeviceFinalize_Private)); 651 652 PetscCall(PetscDeviceInitializeQueryOptions_Private(comm, &deviceContextInitDevice, &defaultInitType, &defaultDeviceId, &defaultDeviceIdSet, &defaultView)); 653 654 // the precise values don't matter here, so long as they are sequential 655 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_HOST) == 0, ""); 656 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_CUDA) == 1, ""); 657 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_HIP) == 2, ""); 658 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_SYCL) == 3, ""); 659 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_MAX) == 4, ""); 660 for (int i = PETSC_DEVICE_HOST; i < PETSC_DEVICE_MAX; ++i) { 661 const auto deviceType = PetscDeviceTypeCast(i); 662 auto initType = defaultInitType; 663 664 PetscCall(PetscDeviceInitializeTypeFromOptions_Private(comm, deviceType, defaultDeviceId, defaultView, &initType)); 665 if (PetscDeviceConfiguredFor_Internal(deviceType)) { 666 if (initType == PETSC_DEVICE_INIT_EAGER) { 667 initializeDeviceContextEagerly = PETSC_TRUE; 668 // only update the default device if the user hasn't set it previously 669 if (!defaultDeviceIdSet) { 670 deviceContextInitDevice = deviceType; 671 PetscCall(PetscInfo(nullptr, "PetscDevice %s set as default device type due to eager initialization\n", PetscDeviceTypes[deviceType])); 672 } 673 } else if (initType == PETSC_DEVICE_INIT_NONE) { 674 if (deviceType != PETSC_DEVICE_HOST) PetscCheck(!defaultDeviceIdSet || (deviceType != deviceContextInitDevice), comm, PETSC_ERR_USER_INPUT, "Cannot explicitly disable the device set as default device type (%s)", PetscDeviceTypes[deviceType]); 675 } 676 } 677 } 678 679 PetscCall(PetscDeviceSetDefaultDeviceType(deviceContextInitDevice)); 680 PetscCall(PetscDeviceContextSetRootDeviceType_Internal(PETSC_DEVICE_DEFAULT())); 681 /* ----------------------------------------------------------------------------------- */ 682 /* PetscDevice is now fully initialized */ 683 /* ----------------------------------------------------------------------------------- */ 684 { 685 /* 686 query the options db to get the root settings from the user (if any). 687 688 This section is a bit of a hack. We have to reach across to dcontext.cxx to all but call 689 PetscDeviceContextSetFromOptions() before we even have one, then set a few static 690 variables in that file with the results. 691 */ 692 auto dtype = std::make_pair(PETSC_DEVICE_DEFAULT(), PETSC_FALSE); 693 auto stype = std::make_pair(PETSC_DEVICE_CONTEXT_DEFAULT_STREAM_TYPE, PETSC_FALSE); 694 695 PetscOptionsBegin(comm, "root_", "Root PetscDeviceContext Options", "Sys"); 696 PetscCall(PetscDeviceContextQueryOptions_Internal(PetscOptionsObject, dtype, stype)); 697 PetscOptionsEnd(); 698 699 if (dtype.second) PetscCall(PetscDeviceContextSetRootDeviceType_Internal(dtype.first)); 700 if (stype.second) PetscCall(PetscDeviceContextSetRootStreamType_Internal(stype.first)); 701 } 702 703 if (initializeDeviceContextEagerly) { 704 PetscDeviceContext dctx; 705 706 PetscCall(PetscInfo(nullptr, "Eagerly initializing PetscDeviceContext with %s device\n", PetscDeviceTypes[deviceContextInitDevice])); 707 /* instantiates the device context */ 708 PetscCall(PetscDeviceContextGetCurrentContext(&dctx)); 709 PetscCall(PetscDeviceContextSetUp(dctx)); 710 } 711 PetscFunctionReturn(PETSC_SUCCESS); 712 } 713