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 PetscValidPointer(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 PetscValidPointer(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 PetscCall(PetscViewerFlush(viewer)); 255 } 256 PetscFunctionReturn(PETSC_SUCCESS); 257 } 258 259 /*@C 260 PetscDeviceGetType - Get the type of device 261 262 Not Collective 263 264 Input Parameter: 265 . device - The `PetscDevice` 266 267 Output Parameter: 268 . type - The `PetscDeviceType` 269 270 Level: beginner 271 272 .seealso: `PetscDevice`, `PetscDeviceType`, `PetscDeviceSetDefaultDeviceType()`, 273 `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceDestroy()`, 274 `PetscDeviceGetDeviceId()`, `PETSC_DEVICE_DEFAULT()` 275 @*/ 276 PetscErrorCode PetscDeviceGetType(PetscDevice device, PetscDeviceType *type) 277 { 278 PetscFunctionBegin; 279 PetscValidDevice(device, 1); 280 PetscValidPointer(type, 2); 281 *type = device->type; 282 PetscFunctionReturn(PETSC_SUCCESS); 283 } 284 285 /*@C 286 PetscDeviceGetDeviceId - Get the device ID for a `PetscDevice` 287 288 Not Collective 289 290 Input Parameter: 291 . device - The `PetscDevice` 292 293 Output Parameter: 294 . id - The id 295 296 Level: beginner 297 298 Notes: 299 The returned ID may have been assigned by the underlying device backend. For example if the 300 backend is CUDA then `id` is exactly the value returned by `cudaGetDevice()` at the time when 301 this device was configured. 302 303 .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceGetType()` 304 @*/ 305 PetscErrorCode PetscDeviceGetDeviceId(PetscDevice device, PetscInt *id) 306 { 307 PetscFunctionBegin; 308 PetscValidDevice(device, 1); 309 PetscValidIntPointer(id, 2); 310 *id = device->deviceId; 311 PetscFunctionReturn(PETSC_SUCCESS); 312 } 313 314 namespace 315 { 316 317 struct DefaultDeviceType : public Petsc::RegisterFinalizeable<DefaultDeviceType> { 318 PetscDeviceType type = PETSC_DEVICE_HARDWARE_DEFAULT_TYPE; 319 320 PetscErrorCode finalize_() noexcept 321 { 322 PetscFunctionBegin; 323 type = PETSC_DEVICE_HARDWARE_DEFAULT_TYPE; 324 PetscFunctionReturn(PETSC_SUCCESS); 325 } 326 }; 327 328 auto default_device_type = DefaultDeviceType(); 329 330 } // namespace 331 332 /*@C 333 PETSC_DEVICE_DEFAULT - Retrieve the current default `PetscDeviceType` 334 335 Not Collective 336 337 Level: beginner 338 339 Notes: 340 Unless selected by the user, the default device is selected in the following order\: 341 `PETSC_DEVICE_HIP`, `PETSC_DEVICE_CUDA`, `PETSC_DEVICE_SYCL`, `PETSC_DEVICE_HOST`. 342 343 .seealso: `PetscDeviceType`, `PetscDeviceSetDefaultDeviceType()`, `PetscDeviceGetType()` 344 @*/ 345 PetscDeviceType PETSC_DEVICE_DEFAULT(void) 346 { 347 return default_device_type.type; 348 } 349 350 /*@C 351 PetscDeviceSetDefaultDeviceType - Set the default device type for `PetscDevice` 352 353 Not Collective 354 355 Input Parameter: 356 . type - the new default device type 357 358 Level: beginner 359 360 Notes: 361 This sets the `PetscDeviceType` returned by `PETSC_DEVICE_DEFAULT()`. 362 363 .seealso: `PetscDeviceType`, `PetscDeviceGetType`, 364 @*/ 365 PetscErrorCode PetscDeviceSetDefaultDeviceType(PetscDeviceType type) 366 { 367 PetscFunctionBegin; 368 PetscValidDeviceType(type, 1); 369 if (default_device_type.type != type) { 370 // no need to waster a PetscRegisterFinalize() slot if we don't change it 371 default_device_type.type = type; 372 PetscCall(default_device_type.register_finalize()); 373 } 374 PetscFunctionReturn(PETSC_SUCCESS); 375 } 376 377 namespace 378 { 379 380 std::array<std::pair<PetscDevice, bool>, PETSC_DEVICE_MAX> defaultDevices = {}; 381 382 /* 383 Actual initialization function; any functions claiming to initialize PetscDevice or 384 PetscDeviceContext will have to run through this one 385 */ 386 PetscErrorCode PetscDeviceInitializeDefaultDevice_Internal(PetscDeviceType type, PetscInt defaultDeviceId) 387 { 388 PetscFunctionBegin; 389 PetscValidDeviceType(type, 1); 390 if (PetscUnlikely(!PetscDeviceInitialized(type))) { 391 auto &dev = defaultDevices[type].first; 392 auto &init = defaultDevices[type].second; 393 394 PetscAssert(!dev, PETSC_COMM_SELF, PETSC_ERR_MEM, "Trying to overwrite existing default device of type %s", PetscDeviceTypes[type]); 395 PetscCall(PetscDeviceCreate(type, defaultDeviceId, &dev)); 396 PetscCall(PetscDeviceConfigure(dev)); 397 init = true; 398 } 399 PetscFunctionReturn(PETSC_SUCCESS); 400 } 401 402 } // namespace 403 404 /*@C 405 PetscDeviceInitialize - Initialize `PetscDevice` 406 407 Not Collective 408 409 Input Parameter: 410 . type - The `PetscDeviceType` to initialize 411 412 Level: beginner 413 414 Notes: 415 Eagerly initializes the corresponding `PetscDeviceType` if needed. If this is the case it may 416 result in device synchronization. 417 418 .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceInitialized()`, 419 `PetscDeviceCreate()`, `PetscDeviceDestroy()` 420 @*/ 421 PetscErrorCode PetscDeviceInitialize(PetscDeviceType type) 422 { 423 PetscFunctionBegin; 424 PetscValidDeviceType(type, 1); 425 PetscCall(PetscDeviceInitializeDefaultDevice_Internal(type, PETSC_DECIDE)); 426 PetscFunctionReturn(PETSC_SUCCESS); 427 } 428 429 /*@C 430 PetscDeviceInitialized - Determines whether `PetscDevice` is initialized for a particular 431 `PetscDeviceType` 432 433 Not Collective 434 435 Input Parameter: 436 . type - The `PetscDeviceType` to check 437 438 Level: beginner 439 440 Notes: 441 Returns `PETSC_TRUE` if `type` is initialized, `PETSC_FALSE` otherwise. 442 443 If one has not configured PETSc for a particular `PetscDeviceType` then this routine will 444 return `PETSC_FALSE` for that `PetscDeviceType`. 445 446 .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceInitialize()`, 447 `PetscDeviceCreate()`, `PetscDeviceDestroy()` 448 @*/ 449 PetscBool PetscDeviceInitialized(PetscDeviceType type) 450 { 451 return static_cast<PetscBool>(PetscDeviceConfiguredFor_Internal(type) && defaultDevices[type].second); 452 } 453 454 /* Get the default PetscDevice for a particular type and constructs them if lazily initialized. */ 455 PetscErrorCode PetscDeviceGetDefaultForType_Internal(PetscDeviceType type, PetscDevice *device) 456 { 457 PetscFunctionBegin; 458 PetscValidPointer(device, 2); 459 PetscCall(PetscDeviceInitialize(type)); 460 *device = defaultDevices[type].first; 461 PetscFunctionReturn(PETSC_SUCCESS); 462 } 463 464 /*@C 465 PetscDeviceGetAttribute - Query a particular attribute of a `PetscDevice` 466 467 Not Collective 468 469 Input Parameters: 470 + device - The `PetscDevice` 471 - attr - The attribute 472 473 Output Parameter: 474 . value - The value of the attribute 475 476 Level: intermediate 477 478 Notes: 479 Since different attributes are often different types `value` is a `void *` to accommodate 480 them all. The underlying type of the attribute is therefore included in the name of the 481 `PetscDeviceAttribute` responsible for querying it. For example, 482 `PETSC_DEVICE_ATTR_SIZE_T_SHARED_MEM_PER_BLOCK` is of type `size_t`. 483 484 .seealso: `PetscDeviceAtrtibute`, `PetscDeviceConfigure()`, `PetscDevice` 485 @*/ 486 PetscErrorCode PetscDeviceGetAttribute(PetscDevice device, PetscDeviceAttribute attr, void *value) 487 { 488 PetscFunctionBegin; 489 PetscValidDevice(device, 1); 490 PetscValidDeviceAttribute(attr, 2); 491 PetscValidPointer(value, 3); 492 PetscUseTypeMethod(device, getattribute, attr, value); 493 PetscFunctionReturn(PETSC_SUCCESS); 494 } 495 496 namespace 497 { 498 499 PetscErrorCode PetscDeviceInitializeTypeFromOptions_Private(MPI_Comm comm, PetscDeviceType type, PetscInt defaultDeviceId, PetscBool defaultView, PetscDeviceInitType *defaultInitType) 500 { 501 PetscFunctionBegin; 502 if (!PetscDeviceConfiguredFor_Internal(type)) { 503 PetscCall(PetscInfo(nullptr, "PetscDeviceType %s not available\n", PetscDeviceTypes[type])); 504 defaultDevices[type].first = nullptr; 505 PetscFunctionReturn(PETSC_SUCCESS); 506 } 507 PetscCall(PetscInfo(nullptr, "PetscDeviceType %s available, initializing\n", PetscDeviceTypes[type])); 508 /* ugly switch needed to pick the right global variable... could maybe do this as a union? */ 509 switch (type) { 510 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HOST, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType); 511 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType); 512 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType); 513 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(SYCL, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType); 514 default: 515 SETERRQ(comm, PETSC_ERR_PLIB, "PETSc was seemingly configured for PetscDeviceType %s but we've fallen through all cases in a switch", PetscDeviceTypes[type]); 516 } 517 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)])); 518 /* 519 defaultInitType, defaultView and defaultDeviceId now represent what the individual TYPES 520 have decided to initialize as 521 */ 522 if ((*defaultInitType == PETSC_DEVICE_INIT_EAGER) || defaultView) { 523 PetscCall(PetscInfo(nullptr, "Eagerly initializing %s PetscDevice\n", PetscDeviceTypes[type])); 524 PetscCall(PetscDeviceInitializeDefaultDevice_Internal(type, defaultDeviceId)); 525 if (defaultView) PetscCall(PetscDeviceView(defaultDevices[type].first, nullptr)); 526 } 527 PetscFunctionReturn(PETSC_SUCCESS); 528 } 529 530 PetscErrorCode PetscDeviceInitializeQueryOptions_Private(MPI_Comm comm, PetscDeviceType *deviceContextInitDevice, PetscDeviceInitType *defaultInitType, PetscInt *defaultDevice, PetscBool *defaultDeviceSet, PetscBool *defaultView) 531 { 532 PetscInt initIdx = PETSC_DEVICE_INIT_LAZY; 533 auto initDeviceIdx = static_cast<PetscInt>(*deviceContextInitDevice); 534 auto flg = PETSC_FALSE; 535 536 PetscFunctionBegin; 537 PetscCall(PetscOptionsHasName(nullptr, nullptr, "-log_view_gpu_time", &flg)); 538 if (flg) PetscCall(PetscLogGpuTime()); 539 540 PetscOptionsBegin(comm, nullptr, "PetscDevice Options", "Sys"); 541 PetscCall(PetscOptionsEList("-device_enable", "How (or whether) to initialize PetscDevices", "PetscDeviceInitialize()", PetscDeviceInitTypes, 3, PetscDeviceInitTypes[initIdx], &initIdx, nullptr)); 542 PetscCall(PetscOptionsEList("-default_device_type", "Set the PetscDeviceType returned by PETSC_DEVICE_DEFAULT()", "PetscDeviceSetDefaultDeviceType()", PetscDeviceTypes, PETSC_DEVICE_MAX, PetscDeviceTypes[initDeviceIdx], &initDeviceIdx, defaultDeviceSet)); 543 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)); 544 PetscCall(PetscOptionsBool("-device_view", "Display device information and assignments (forces eager initialization)", "PetscDeviceView()", *defaultView, defaultView, &flg)); 545 PetscOptionsEnd(); 546 547 if (initIdx == PETSC_DEVICE_INIT_NONE) { 548 /* disabled all device initialization if devices are globally disabled */ 549 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"); 550 *defaultView = PETSC_FALSE; 551 initDeviceIdx = PETSC_DEVICE_HOST; 552 } else { 553 *defaultView = static_cast<PetscBool>(*defaultView && flg); 554 if (*defaultView) initIdx = PETSC_DEVICE_INIT_EAGER; 555 } 556 *defaultInitType = PetscDeviceInitTypeCast(initIdx); 557 *deviceContextInitDevice = PetscDeviceTypeCast(initDeviceIdx); 558 PetscFunctionReturn(PETSC_SUCCESS); 559 } 560 561 /* called from PetscFinalize() do not call yourself! */ 562 PetscErrorCode PetscDeviceFinalize_Private() 563 { 564 PetscFunctionBegin; 565 if (PetscDefined(USE_DEBUG)) { 566 /* 567 you might be thinking, why on earth are you registered yet another finalizer in a 568 function already called during PetscRegisterFinalizeAll()? If this seems stupid it's 569 because it is. 570 571 The crux of the problem is that the initializer (and therefore the ~finalizer~) of 572 PetscDeviceContext is guaranteed to run after PetscDevice's. So if the global context had 573 a default PetscDevice attached, that PetscDevice will have a reference count >0 and hence 574 won't be destroyed yet. So we need to repeat the check that all devices have been 575 destroyed again ~after~ the global context is destroyed. In summary: 576 577 1. This finalizer runs and destroys all devices, except it may not because the global 578 context may still hold a reference! 579 2. The global context finalizer runs and does the final reference count decrement 580 required, which actually destroys the held device. 581 3. Our newly added finalizer runs and checks that all is well. 582 */ 583 PetscCall(PetscRegisterFinalize([] { 584 PetscFunctionBegin; 585 for (auto &&device : defaultDevices) { 586 const auto dev = device.first; 587 588 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); 589 } 590 PetscFunctionReturn(PETSC_SUCCESS); 591 })); 592 } 593 for (auto &&device : defaultDevices) { 594 PetscCall(PetscDeviceDestroy(&device.first)); 595 device.second = false; 596 } 597 PetscFunctionReturn(PETSC_SUCCESS); 598 } 599 600 } // namespace 601 602 /* 603 Begins the init proceeedings for the entire PetscDevice stack. there are 3 stages of 604 initialization types: 605 606 1. defaultInitType - how does PetscDevice as a whole expect to initialize? 607 2. subTypeDefaultInitType - how does each PetscDevice implementation expect to initialize? 608 e.g. you may want to blanket disable PetscDevice init (and disable say Kokkos init), but 609 have all CUDA devices still initialize. 610 611 All told the following happens: 612 613 0. defaultInitType -> LAZY 614 1. Check for log_view/log_summary, if yes defaultInitType -> EAGER 615 2. PetscDevice initializes each sub type with deviceDefaultInitType. 616 2.1 Each enabled PetscDevice sub-type then does the above disable or view check in addition 617 to checking for specific device init. if view or specific device init 618 subTypeDefaultInitType -> EAGER. disabled once again overrides all. 619 */ 620 621 PetscErrorCode PetscDeviceInitializeFromOptions_Internal(MPI_Comm comm) 622 { 623 auto defaultView = PETSC_FALSE; 624 auto initializeDeviceContextEagerly = PETSC_FALSE; 625 auto defaultDeviceSet = PETSC_FALSE; 626 auto defaultDevice = PetscInt{PETSC_DECIDE}; 627 auto deviceContextInitDevice = PETSC_DEVICE_DEFAULT(); 628 auto defaultInitType = PETSC_DEVICE_INIT_LAZY; 629 630 PetscFunctionBegin; 631 if (PetscDefined(USE_DEBUG)) { 632 int result; 633 634 PetscCallMPI(MPI_Comm_compare(comm, PETSC_COMM_WORLD, &result)); 635 /* in order to accurately assign ranks to gpus we need to get the MPI_Comm_rank of the 636 * global space */ 637 if (PetscUnlikely(result != MPI_IDENT)) { 638 char name[MPI_MAX_OBJECT_NAME] = {}; 639 int len; /* unused */ 640 641 PetscCallMPI(MPI_Comm_get_name(comm, name, &len)); 642 SETERRQ(comm, PETSC_ERR_MPI, "Default devices being initialized on MPI_Comm '%s' not PETSC_COMM_WORLD", name); 643 } 644 } 645 comm = PETSC_COMM_WORLD; /* from this point on we assume we're on PETSC_COMM_WORLD */ 646 PetscCall(PetscRegisterFinalize(PetscDeviceFinalize_Private)); 647 648 PetscCall(PetscDeviceInitializeQueryOptions_Private(comm, &deviceContextInitDevice, &defaultInitType, &defaultDevice, &defaultDeviceSet, &defaultView)); 649 650 // the precise values don't matter here, so long as they are sequential 651 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_HOST) == 0, ""); 652 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_CUDA) == 1, ""); 653 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_HIP) == 2, ""); 654 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_SYCL) == 3, ""); 655 static_assert(Petsc::util::to_underlying(PETSC_DEVICE_MAX) == 4, ""); 656 for (int i = PETSC_DEVICE_HOST; i < PETSC_DEVICE_MAX; ++i) { 657 const auto deviceType = PetscDeviceTypeCast(i); 658 auto initType = defaultInitType; 659 660 PetscCall(PetscDeviceInitializeTypeFromOptions_Private(comm, deviceType, defaultDevice, defaultView, &initType)); 661 if (PetscDeviceConfiguredFor_Internal(deviceType)) { 662 if (initType == PETSC_DEVICE_INIT_EAGER) { 663 initializeDeviceContextEagerly = PETSC_TRUE; 664 // only update the default device if the user hasn't set it previously 665 if (!defaultDeviceSet) { 666 deviceContextInitDevice = deviceType; 667 PetscCall(PetscInfo(nullptr, "PetscDevice %s set as default device type due to eager initialization\n", PetscDeviceTypes[deviceType])); 668 } 669 } else if (initType == PETSC_DEVICE_INIT_NONE) { 670 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]); 671 } 672 } 673 } 674 675 PetscCall(PetscDeviceSetDefaultDeviceType(deviceContextInitDevice)); 676 PetscCall(PetscDeviceContextSetRootDeviceType_Internal(PETSC_DEVICE_DEFAULT())); 677 /* ----------------------------------------------------------------------------------- */ 678 /* PetscDevice is now fully initialized */ 679 /* ----------------------------------------------------------------------------------- */ 680 { 681 /* 682 query the options db to get the root settings from the user (if any). 683 684 This section is a bit of a hack. We have to reach across to dcontext.cxx to all but call 685 PetscDeviceContextSetFromOptions() before we even have one, then set a few static 686 variables in that file with the results. 687 */ 688 auto dtype = std::make_pair(PETSC_DEVICE_DEFAULT(), PETSC_FALSE); 689 auto stype = std::make_pair(PETSC_DEVICE_CONTEXT_DEFAULT_STREAM_TYPE, PETSC_FALSE); 690 691 PetscOptionsBegin(comm, "root_", "Root PetscDeviceContext Options", "Sys"); 692 PetscCall(PetscDeviceContextQueryOptions_Internal(PetscOptionsObject, dtype, stype)); 693 PetscOptionsEnd(); 694 695 if (dtype.second) PetscCall(PetscDeviceContextSetRootDeviceType_Internal(dtype.first)); 696 if (stype.second) PetscCall(PetscDeviceContextSetRootStreamType_Internal(stype.first)); 697 } 698 699 if (initializeDeviceContextEagerly) { 700 PetscDeviceContext dctx; 701 702 PetscCall(PetscInfo(nullptr, "Eagerly initializing PetscDeviceContext with %s device\n", PetscDeviceTypes[deviceContextInitDevice])); 703 /* instantiates the device context */ 704 PetscCall(PetscDeviceContextGetCurrentContext(&dctx)); 705 PetscCall(PetscDeviceContextSetUp(dctx)); 706 } 707 PetscFunctionReturn(PETSC_SUCCESS); 708 } 709