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