xref: /petsc/src/sys/objects/device/interface/device.cxx (revision a4af0ceea8a251db97ee0dc5c0d52d4adf50264a)
1030f984aSJacob Faibussowitsch #include "cupmdevice.hpp" /* I "petscdevice.h" */
2030f984aSJacob Faibussowitsch 
3030f984aSJacob Faibussowitsch using namespace Petsc;
4030f984aSJacob Faibussowitsch 
5*a4af0ceeSJacob Faibussowitsch /* note to anyone adding more classes, the name must be ALL_CAPS_SHORT_NAME + Device exactly to
6*a4af0ceeSJacob Faibussowitsch  * be picked up by the switch-case macros below. */
7030f984aSJacob Faibussowitsch #if PetscDefined(HAVE_CUDA)
8*a4af0ceeSJacob Faibussowitsch static CUPMDevice<CUPMDeviceType::CUDA> CUDADevice(PetscDeviceContextCreate_CUDA);
9030f984aSJacob Faibussowitsch #endif
10030f984aSJacob Faibussowitsch #if PetscDefined(HAVE_HIP)
11*a4af0ceeSJacob Faibussowitsch static CUPMDevice<CUPMDeviceType::HIP>  HIPDevice(PetscDeviceContextCreate_HIP);
12030f984aSJacob Faibussowitsch #endif
13030f984aSJacob Faibussowitsch 
14*a4af0ceeSJacob Faibussowitsch const char *const PetscDeviceTypes[] = {
15*a4af0ceeSJacob Faibussowitsch   "invalid",
16*a4af0ceeSJacob Faibussowitsch   "cuda",
17*a4af0ceeSJacob Faibussowitsch   "hip",
18*a4af0ceeSJacob Faibussowitsch   "max",
19*a4af0ceeSJacob Faibussowitsch   "PetscDeviceType",
20*a4af0ceeSJacob Faibussowitsch   "PETSC_DEVICE_",
21*a4af0ceeSJacob Faibussowitsch   PETSC_NULLPTR
22*a4af0ceeSJacob Faibussowitsch };
23*a4af0ceeSJacob Faibussowitsch 
24*a4af0ceeSJacob Faibussowitsch const char *const PetscDeviceInitTypes[] = {
25*a4af0ceeSJacob Faibussowitsch   "none",
26*a4af0ceeSJacob Faibussowitsch   "lazy",
27*a4af0ceeSJacob Faibussowitsch   "eager",
28*a4af0ceeSJacob Faibussowitsch   "PetscDeviceInitType",
29*a4af0ceeSJacob Faibussowitsch   "PETSC_DEVICE_INIT_",
30*a4af0ceeSJacob Faibussowitsch   PETSC_NULLPTR
31*a4af0ceeSJacob Faibussowitsch };
32*a4af0ceeSJacob Faibussowitsch static_assert(
33*a4af0ceeSJacob Faibussowitsch   sizeof(PetscDeviceInitTypes)/sizeof(*PetscDeviceInitTypes) == 6,
34*a4af0ceeSJacob Faibussowitsch   "Must change CUPMDevice<T>::initialize number of enum values in -device_enable_cupm to match!"
35*a4af0ceeSJacob Faibussowitsch );
36*a4af0ceeSJacob Faibussowitsch 
37*a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_DEFAULT_CASE(comm,type)                            \
38*a4af0ceeSJacob Faibussowitsch   SETERRQ1((comm),PETSC_ERR_PLIB,                                       \
39*a4af0ceeSJacob Faibussowitsch            "PETSc was seemingly configured for PetscDeviceType %s but " \
40*a4af0ceeSJacob Faibussowitsch            "we've fallen through all cases in a switch",                \
41*a4af0ceeSJacob Faibussowitsch            PetscDeviceTypes[type])
42*a4af0ceeSJacob Faibussowitsch 
43*a4af0ceeSJacob Faibussowitsch #define CAT_(a,...) a ## __VA_ARGS__
44*a4af0ceeSJacob Faibussowitsch #define CAT(a,...)  CAT_(a,__VA_ARGS__)
45*a4af0ceeSJacob Faibussowitsch 
46*a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED__0(IMPLS,func,...)
47*a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED__1(IMPLS,func,...)           \
48*a4af0ceeSJacob Faibussowitsch   case CAT(PETSC_DEVICE_,IMPLS):                                        \
49*a4af0ceeSJacob Faibussowitsch   {                                                                     \
50*a4af0ceeSJacob Faibussowitsch     auto ierr = CAT(IMPLS,Device).func(__VA_ARGS__);CHKERRQ(ierr);      \
51*a4af0ceeSJacob Faibussowitsch     break;                                                              \
52*a4af0ceeSJacob Faibussowitsch   }
53*a4af0ceeSJacob Faibussowitsch 
54*a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED_(IMPLS,...)                  \
55*a4af0ceeSJacob Faibussowitsch   CAT(PETSC_DEVICE_CASE_IF_PETSC_DEFINED__,PetscDefined(CAT(HAVE_,IMPLS)))(IMPLS,__VA_ARGS__)
56*a4af0ceeSJacob Faibussowitsch 
57*a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED(IMPLS,...)           \
58*a4af0ceeSJacob Faibussowitsch   PETSC_DEVICE_CASE_IF_PETSC_DEFINED_(IMPLS,__VA_ARGS__)
59*a4af0ceeSJacob Faibussowitsch 
60*a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_UNUSED_IF_NO_DEVICE(var) (void)(var)
61030f984aSJacob Faibussowitsch 
62030f984aSJacob Faibussowitsch /*@C
63*a4af0ceeSJacob Faibussowitsch   PetscDeviceCreate - Get a new handle for a particular device type
64030f984aSJacob Faibussowitsch 
65030f984aSJacob Faibussowitsch   Not Collective, Possibly Synchronous
66030f984aSJacob Faibussowitsch 
67030f984aSJacob Faibussowitsch   Input Parameter:
68*a4af0ceeSJacob Faibussowitsch . type  - The type of PetscDevice
69*a4af0ceeSJacob Faibussowitsch . devid - The numeric ID# of the device (pass PETSC_DECIDE to assign automatically)
70030f984aSJacob Faibussowitsch 
71030f984aSJacob Faibussowitsch   Output Parameter:
72030f984aSJacob Faibussowitsch . device - The PetscDevice
73030f984aSJacob Faibussowitsch 
74030f984aSJacob Faibussowitsch   Notes:
75*a4af0ceeSJacob Faibussowitsch   This routine may initialize PetscDevice. If this is the case, this will most likely cause
76*a4af0ceeSJacob Faibussowitsch   some sort of device synchronization.
77*a4af0ceeSJacob Faibussowitsch 
78*a4af0ceeSJacob Faibussowitsch   devid is what you might pass to cudaSetDevice() for example.
79030f984aSJacob Faibussowitsch 
80030f984aSJacob Faibussowitsch   Level: beginner
81030f984aSJacob Faibussowitsch 
82*a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceInitType, PetscDeviceInitialize(),
83*a4af0ceeSJacob Faibussowitsch PetscDeviceInitialized(), PetscDeviceConfigure(), PetscDeviceView(), PetscDeviceDestroy()
84030f984aSJacob Faibussowitsch @*/
85*a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceCreate(PetscDeviceType type, PetscInt devid, PetscDevice *device)
86030f984aSJacob Faibussowitsch {
87030f984aSJacob Faibussowitsch   static PetscInt PetscDeviceCounter = 0;
88030f984aSJacob Faibussowitsch   PetscDevice     dev;
89030f984aSJacob Faibussowitsch   PetscErrorCode  ierr;
90030f984aSJacob Faibussowitsch 
91030f984aSJacob Faibussowitsch   PetscFunctionBegin;
92*a4af0ceeSJacob Faibussowitsch   PetscValidDeviceType(type,1);
93*a4af0ceeSJacob Faibussowitsch   PetscValidPointer(device,3);
94*a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceInitializePackage();CHKERRQ(ierr);
95030f984aSJacob Faibussowitsch   ierr = PetscNew(&dev);CHKERRQ(ierr);
96030f984aSJacob Faibussowitsch   dev->id     = PetscDeviceCounter++;
97*a4af0ceeSJacob Faibussowitsch   dev->type   = type;
98*a4af0ceeSJacob Faibussowitsch   dev->refcnt = 1;
99*a4af0ceeSJacob Faibussowitsch   /* if you are adding a device, you also need to add it's initialization in
100*a4af0ceeSJacob Faibussowitsch      PetscDeviceInitializeTypeFromOptions_Private() below */
101*a4af0ceeSJacob Faibussowitsch   switch (type) {
102*a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA,getDevice,dev,devid);
103*a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP,getDevice,dev,devid);
104030f984aSJacob Faibussowitsch   default:
105*a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_UNUSED_IF_NO_DEVICE(devid);
106*a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_DEFAULT_CASE(PETSC_COMM_SELF,type);
107030f984aSJacob Faibussowitsch   }
108030f984aSJacob Faibussowitsch   *device = dev;
109030f984aSJacob Faibussowitsch   PetscFunctionReturn(0);
110030f984aSJacob Faibussowitsch }
111030f984aSJacob Faibussowitsch 
112030f984aSJacob Faibussowitsch /*@C
113030f984aSJacob Faibussowitsch   PetscDeviceDestroy - Free a PetscDevice
114030f984aSJacob Faibussowitsch 
115030f984aSJacob Faibussowitsch   Not Collective, Asynchronous
116030f984aSJacob Faibussowitsch 
117030f984aSJacob Faibussowitsch   Input Parameter:
118030f984aSJacob Faibussowitsch . device - The PetscDevice
119030f984aSJacob Faibussowitsch 
120030f984aSJacob Faibussowitsch   Level: beginner
121030f984aSJacob Faibussowitsch 
122*a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceCreate(), PetscDeviceConfigure(), PetscDeviceView()
123030f984aSJacob Faibussowitsch @*/
124030f984aSJacob Faibussowitsch PetscErrorCode PetscDeviceDestroy(PetscDevice *device)
125030f984aSJacob Faibussowitsch {
126030f984aSJacob Faibussowitsch   PetscErrorCode ierr;
127030f984aSJacob Faibussowitsch 
128*a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
129*a4af0ceeSJacob Faibussowitsch   if (!*device) PetscFunctionReturn(0);
130*a4af0ceeSJacob Faibussowitsch   PetscValidDevice(*device,1);
131*a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceDereference_Internal(*device);CHKERRQ(ierr);
132*a4af0ceeSJacob Faibussowitsch   if ((*device)->refcnt) {
133*a4af0ceeSJacob Faibussowitsch     *device = PETSC_NULLPTR;
134*a4af0ceeSJacob Faibussowitsch     PetscFunctionReturn(0);
135030f984aSJacob Faibussowitsch   }
136*a4af0ceeSJacob Faibussowitsch   ierr = PetscFree((*device)->data);CHKERRQ(ierr);
137*a4af0ceeSJacob Faibussowitsch   ierr = PetscFree(*device);CHKERRQ(ierr);
138030f984aSJacob Faibussowitsch   PetscFunctionReturn(0);
139030f984aSJacob Faibussowitsch }
140030f984aSJacob Faibussowitsch 
141*a4af0ceeSJacob Faibussowitsch /*@C
142*a4af0ceeSJacob Faibussowitsch   PetscDeviceConfigure - Configure a particular PetscDevice
143030f984aSJacob Faibussowitsch 
144*a4af0ceeSJacob Faibussowitsch   Not Collective, Asynchronous
145*a4af0ceeSJacob Faibussowitsch 
146*a4af0ceeSJacob Faibussowitsch   Input Parameter:
147*a4af0ceeSJacob Faibussowitsch . device - The PetscDevice to configure
148*a4af0ceeSJacob Faibussowitsch 
149*a4af0ceeSJacob Faibussowitsch   Notes:
150*a4af0ceeSJacob Faibussowitsch   The user should not assume that this is a cheap operation
151*a4af0ceeSJacob Faibussowitsch 
152*a4af0ceeSJacob Faibussowitsch   Level: beginner
153*a4af0ceeSJacob Faibussowitsch 
154*a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceCreate(), PetscDeviceView(), PetscDeviceDestroy()
155*a4af0ceeSJacob Faibussowitsch @*/
156*a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceConfigure(PetscDevice device)
157030f984aSJacob Faibussowitsch {
158030f984aSJacob Faibussowitsch   PetscErrorCode ierr;
159030f984aSJacob Faibussowitsch 
160030f984aSJacob Faibussowitsch   PetscFunctionBegin;
161*a4af0ceeSJacob Faibussowitsch   PetscValidDevice(device,1);
162*a4af0ceeSJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
163*a4af0ceeSJacob Faibussowitsch     /* if no available configuration is available, this cascades all the way down to default
164*a4af0ceeSJacob Faibussowitsch        and error */
165*a4af0ceeSJacob Faibussowitsch     switch (device->type) {
166*a4af0ceeSJacob Faibussowitsch     case PETSC_DEVICE_CUDA: if (PetscDefined(HAVE_CUDA)) break;
167*a4af0ceeSJacob Faibussowitsch     case PETSC_DEVICE_HIP:  if (PetscDefined(HAVE_HIP))  break;
168*a4af0ceeSJacob Faibussowitsch     default:
169*a4af0ceeSJacob Faibussowitsch       PETSC_DEVICE_DEFAULT_CASE(PETSC_COMM_SELF,device->type);
170*a4af0ceeSJacob Faibussowitsch       break;
171*a4af0ceeSJacob Faibussowitsch     }
172*a4af0ceeSJacob Faibussowitsch   }
173*a4af0ceeSJacob Faibussowitsch   ierr = (*device->ops->configure)(device);CHKERRQ(ierr);
174*a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
175*a4af0ceeSJacob Faibussowitsch }
176*a4af0ceeSJacob Faibussowitsch 
177*a4af0ceeSJacob Faibussowitsch /*@C
178*a4af0ceeSJacob Faibussowitsch   PetscDeviceView - View a PetscDevice
179*a4af0ceeSJacob Faibussowitsch 
180*a4af0ceeSJacob Faibussowitsch   Collective on viewer, Asynchronous
181*a4af0ceeSJacob Faibussowitsch 
182*a4af0ceeSJacob Faibussowitsch   Input Parameter:
183*a4af0ceeSJacob Faibussowitsch + device - The PetscDevice to configure
184*a4af0ceeSJacob Faibussowitsch - viewer - The PetscViewer to view the device with (NULL for PETSC_VIEWER_STDOUT_WORLD)
185*a4af0ceeSJacob Faibussowitsch 
186*a4af0ceeSJacob Faibussowitsch   Level: beginner
187*a4af0ceeSJacob Faibussowitsch 
188*a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceCreate(), PetscDeviceConfigure(), PetscDeviceDestroy()
189*a4af0ceeSJacob Faibussowitsch @*/
190*a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceView(PetscDevice device, PetscViewer viewer)
191*a4af0ceeSJacob Faibussowitsch {
192*a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
193*a4af0ceeSJacob Faibussowitsch 
194*a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
195*a4af0ceeSJacob Faibussowitsch   PetscValidDevice(device,1);
196*a4af0ceeSJacob Faibussowitsch   if (!viewer) {ierr = PetscViewerASCIIGetStdout(PETSC_COMM_WORLD,&viewer);CHKERRQ(ierr);}
197*a4af0ceeSJacob Faibussowitsch   PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
198*a4af0ceeSJacob Faibussowitsch   ierr = (*device->ops->view)(device,viewer);CHKERRQ(ierr);
199*a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
200*a4af0ceeSJacob Faibussowitsch }
201*a4af0ceeSJacob Faibussowitsch 
202*a4af0ceeSJacob Faibussowitsch static std::array<bool,PETSC_DEVICE_MAX>        initializedDevice = {};
203*a4af0ceeSJacob Faibussowitsch static std::array<PetscDevice,PETSC_DEVICE_MAX> defaultDevices    = {};
204*a4af0ceeSJacob Faibussowitsch static_assert(initializedDevice.size() == defaultDevices.size(),"");
205*a4af0ceeSJacob Faibussowitsch 
206*a4af0ceeSJacob Faibussowitsch /*@C
207*a4af0ceeSJacob Faibussowitsch   PetscDeviceInitialize - Initialize PetscDevice
208*a4af0ceeSJacob Faibussowitsch 
209*a4af0ceeSJacob Faibussowitsch   Not Collective, Possibly Synchronous
210*a4af0ceeSJacob Faibussowitsch 
211*a4af0ceeSJacob Faibussowitsch   Input Parameter:
212*a4af0ceeSJacob Faibussowitsch . type - The PetscDeviceType to initialize
213*a4af0ceeSJacob Faibussowitsch 
214*a4af0ceeSJacob Faibussowitsch   Notes:
215*a4af0ceeSJacob Faibussowitsch   Eagerly initializes the corresponding PetscDeviceType if needed.
216*a4af0ceeSJacob Faibussowitsch 
217*a4af0ceeSJacob Faibussowitsch   Level: beginner
218*a4af0ceeSJacob Faibussowitsch 
219*a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceInitType, PetscDeviceInitialized(), PetscDeviceCreate(), PetscDeviceDestroy()
220*a4af0ceeSJacob Faibussowitsch @*/
221*a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceInitialize(PetscDeviceType type)
222*a4af0ceeSJacob Faibussowitsch {
223*a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
224*a4af0ceeSJacob Faibussowitsch 
225*a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
226*a4af0ceeSJacob Faibussowitsch   PetscValidDeviceType(type,1);
227*a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceInitializeDefaultDevice_Internal(type,PETSC_DECIDE);CHKERRQ(ierr);
228*a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
229*a4af0ceeSJacob Faibussowitsch }
230*a4af0ceeSJacob Faibussowitsch 
231*a4af0ceeSJacob Faibussowitsch /*@C
232*a4af0ceeSJacob Faibussowitsch   PetscDeviceInitialized - Determines whether PetscDevice is initialized for a particular
233*a4af0ceeSJacob Faibussowitsch   PetscDeviceType
234*a4af0ceeSJacob Faibussowitsch 
235*a4af0ceeSJacob Faibussowitsch   Not Collective, Asynchronous
236*a4af0ceeSJacob Faibussowitsch 
237*a4af0ceeSJacob Faibussowitsch   Input Parameter:
238*a4af0ceeSJacob Faibussowitsch . type - The PetscDeviceType to check
239*a4af0ceeSJacob Faibussowitsch 
240*a4af0ceeSJacob Faibussowitsch   Output Parameter:
241*a4af0ceeSJacob Faibussowitsch . [return value] - PETSC_TRUE if type is initialized, PETSC_FALSE otherwise
242*a4af0ceeSJacob Faibussowitsch 
243*a4af0ceeSJacob Faibussowitsch   Notes:
244*a4af0ceeSJacob Faibussowitsch   If one has not configured PETSc for a particular PetscDeviceType then this routine will
245*a4af0ceeSJacob Faibussowitsch   return PETSC_FALSE for that PetscDeviceType.
246*a4af0ceeSJacob Faibussowitsch 
247*a4af0ceeSJacob Faibussowitsch   Level: beginner
248*a4af0ceeSJacob Faibussowitsch 
249*a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceInitType, PetscDeviceInitialize(), PetscDeviceCreate(), PetscDeviceDestroy()
250*a4af0ceeSJacob Faibussowitsch @*/
251*a4af0ceeSJacob Faibussowitsch PetscBool PetscDeviceInitialized(PetscDeviceType type)
252*a4af0ceeSJacob Faibussowitsch {
253*a4af0ceeSJacob Faibussowitsch   return static_cast<PetscBool>(PetscDeviceConfiguredFor_Internal(type) && initializedDevice[type]);
254*a4af0ceeSJacob Faibussowitsch }
255*a4af0ceeSJacob Faibussowitsch 
256*a4af0ceeSJacob Faibussowitsch /* Actual intialization function; any functions claiming to initialize PetscDevice or
257*a4af0ceeSJacob Faibussowitsch  * PetscDeviceContext will have to run through this one */
258*a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceInitializeDefaultDevice_Internal(PetscDeviceType type, PetscInt defaultDeviceId)
259*a4af0ceeSJacob Faibussowitsch {
260*a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
261*a4af0ceeSJacob Faibussowitsch 
262*a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
263*a4af0ceeSJacob Faibussowitsch   PetscValidDeviceType(type,1);
264*a4af0ceeSJacob Faibussowitsch   if (PetscLikely(PetscDeviceInitialized(type))) PetscFunctionReturn(0);
265*a4af0ceeSJacob Faibussowitsch   if (PetscUnlikelyDebug(defaultDevices[type])) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_MEM,"Trying to overwrite existing default device of type %s",PetscDeviceTypes[type]);
266*a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceCreate(type,defaultDeviceId,&defaultDevices[type]);CHKERRQ(ierr);
267*a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceConfigure(defaultDevices[type]);CHKERRQ(ierr);
268030f984aSJacob Faibussowitsch   /* the default devices are all automatically "referenced" at least once, otherwise the
269*a4af0ceeSJacob Faibussowitsch    * reference counting is off for them. We could alternatively increase the reference count
270*a4af0ceeSJacob Faibussowitsch    * when they are retrieved but that is a lot more brittle; what's to stop someone from doing
271*a4af0ceeSJacob Faibussowitsch    * the following?
272030f984aSJacob Faibussowitsch 
273030f984aSJacob Faibussowitsch    for (int i = 0; i < 10000; ++i) auto device = PetscDeviceDefault_Internal();
274030f984aSJacob Faibussowitsch    */
275*a4af0ceeSJacob Faibussowitsch   initializedDevice[type] = true;
276*a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
277*a4af0ceeSJacob Faibussowitsch }
278*a4af0ceeSJacob Faibussowitsch 
279*a4af0ceeSJacob Faibussowitsch static PetscErrorCode PetscDeviceInitializeTypeFromOptions_Private(MPI_Comm comm, PetscDeviceType type, PetscInt defaultDeviceId, PetscBool defaultView, PetscDeviceInitType *defaultInitType)
280*a4af0ceeSJacob Faibussowitsch {
281*a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
282*a4af0ceeSJacob Faibussowitsch 
283*a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
284*a4af0ceeSJacob Faibussowitsch   if (!PetscDeviceConfiguredFor_Internal(type)) {
285*a4af0ceeSJacob Faibussowitsch     ierr = PetscInfo1(PETSC_NULLPTR,"PetscDeviceType %s not supported\n",PetscDeviceTypes[type]);CHKERRQ(ierr);
286*a4af0ceeSJacob Faibussowitsch     defaultDevices[type] = PETSC_NULLPTR;
287*a4af0ceeSJacob Faibussowitsch     PetscFunctionReturn(0);
288*a4af0ceeSJacob Faibussowitsch   }
289*a4af0ceeSJacob Faibussowitsch   ierr = PetscInfo1(PETSC_NULLPTR,"PetscDeviceType %s supported, initializing\n",PetscDeviceTypes[type]);CHKERRQ(ierr);
290*a4af0ceeSJacob Faibussowitsch   /* ugly switch needed to pick the right global variable... could maybe do this as a union? */
291*a4af0ceeSJacob Faibussowitsch   switch (type) {
292*a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA,initialize,comm,&defaultDeviceId,defaultInitType);
293*a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP,initialize,comm,&defaultDeviceId,defaultInitType);
294*a4af0ceeSJacob Faibussowitsch   default:
295*a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_DEFAULT_CASE(comm,type);
296*a4af0ceeSJacob Faibussowitsch     break;
297*a4af0ceeSJacob Faibussowitsch   }
298*a4af0ceeSJacob Faibussowitsch   /* defaultInitType and defaultDeviceId now represent what the individual TYPES have decided
299*a4af0ceeSJacob Faibussowitsch    * to initialize as */
300*a4af0ceeSJacob Faibussowitsch   if (*defaultInitType == PETSC_DEVICE_INIT_EAGER) {
301*a4af0ceeSJacob Faibussowitsch     ierr = PetscInfo1(PETSC_NULLPTR,"Greedily initializing %s PetscDevice\n",PetscDeviceTypes[type]);CHKERRQ(ierr);
302*a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceInitializeDefaultDevice_Internal(type,defaultDeviceId);CHKERRQ(ierr);
303*a4af0ceeSJacob Faibussowitsch     if (defaultView) {
304*a4af0ceeSJacob Faibussowitsch       PetscViewer vwr;
305*a4af0ceeSJacob Faibussowitsch 
306*a4af0ceeSJacob Faibussowitsch       ierr = PetscViewerASCIIGetStdout(comm,&vwr);CHKERRQ(ierr);
307*a4af0ceeSJacob Faibussowitsch       ierr = PetscDeviceView(defaultDevices[type],vwr);CHKERRQ(ierr);
308*a4af0ceeSJacob Faibussowitsch     }
309030f984aSJacob Faibussowitsch   }
310030f984aSJacob Faibussowitsch   PetscFunctionReturn(0);
311030f984aSJacob Faibussowitsch }
312030f984aSJacob Faibussowitsch 
313030f984aSJacob Faibussowitsch /* called from PetscFinalize() do not call yourself! */
314*a4af0ceeSJacob Faibussowitsch static PetscErrorCode PetscDeviceFinalize_Private(void)
315030f984aSJacob Faibussowitsch {
316030f984aSJacob Faibussowitsch   PetscErrorCode ierr;
317030f984aSJacob Faibussowitsch 
318030f984aSJacob Faibussowitsch   PetscFunctionBegin;
319*a4af0ceeSJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
320*a4af0ceeSJacob Faibussowitsch     const auto PetscDeviceCheckAllDestroyedAfterFinalize = [](){
321*a4af0ceeSJacob Faibussowitsch       PetscFunctionBegin;
322*a4af0ceeSJacob Faibussowitsch       for (const auto &device : defaultDevices) {
323*a4af0ceeSJacob Faibussowitsch         if (PetscUnlikely(device)) SETERRQ2(PETSC_COMM_WORLD,PETSC_ERR_COR,"Device of type '%s' had reference count %D and was not type name(args) const;ully destroyed during PetscFinalize()",PetscDeviceTypes[device->type],device->refcnt);
324*a4af0ceeSJacob Faibussowitsch       }
325*a4af0ceeSJacob Faibussowitsch       PetscFunctionReturn(0);
326*a4af0ceeSJacob Faibussowitsch     };
327*a4af0ceeSJacob Faibussowitsch     /* you might be thinking, why on earth are you registered yet another finalizer in a
328*a4af0ceeSJacob Faibussowitsch      * function already called during PetscRegisterFinalizeAll()? If this seems stupid it's
329*a4af0ceeSJacob Faibussowitsch      * because it is.
330*a4af0ceeSJacob Faibussowitsch      *
331*a4af0ceeSJacob Faibussowitsch      * The crux of the problem is that the initializer (and therefore the ~finalizer~) of
332*a4af0ceeSJacob Faibussowitsch      * PetscDeviceContext is guaranteed to run after this finalizer. So if the global context
333*a4af0ceeSJacob Faibussowitsch      * had a default PetscDevice attached it will hold a reference this routine won't destroy
334*a4af0ceeSJacob Faibussowitsch      * it. So we need to check that all devices have been destroyed after the global context is
335*a4af0ceeSJacob Faibussowitsch      * destroyed. In summary:
336*a4af0ceeSJacob Faibussowitsch      *
337*a4af0ceeSJacob Faibussowitsch      * 1. This finalizer runs and destroys all devices, except it may not because the global
338*a4af0ceeSJacob Faibussowitsch      *    context may still hold a reference!
339*a4af0ceeSJacob Faibussowitsch      * 2. The global context finalizer runs and in turn actually destroys the referenced
340*a4af0ceeSJacob Faibussowitsch      *    device.
341*a4af0ceeSJacob Faibussowitsch      * 3. Our newly added finalizer runs and checks that all is well.
342*a4af0ceeSJacob Faibussowitsch      */
343*a4af0ceeSJacob Faibussowitsch     ierr = PetscRegisterFinalize(PetscDeviceCheckAllDestroyedAfterFinalize);CHKERRQ(ierr);
344*a4af0ceeSJacob Faibussowitsch   }
345*a4af0ceeSJacob Faibussowitsch   for (auto &&device : defaultDevices) {ierr = PetscDeviceDestroy(&device);CHKERRQ(ierr);}
346*a4af0ceeSJacob Faibussowitsch   CHKERRCXX(initializedDevice.fill(false));
347030f984aSJacob Faibussowitsch   PetscFunctionReturn(0);
348030f984aSJacob Faibussowitsch }
349030f984aSJacob Faibussowitsch 
350*a4af0ceeSJacob Faibussowitsch /* begins the init proceeedings for the entire PetscDevice stack. there are 3 stages of
351*a4af0ceeSJacob Faibussowitsch  * initialization types:
352*a4af0ceeSJacob Faibussowitsch  1. defaultInitType - how does PetscDevice as a whole expect to initialize?
353*a4af0ceeSJacob Faibussowitsch  2. subTypeDefaultInitType - how does each PetscDevice implementation expect to initialize?
354*a4af0ceeSJacob Faibussowitsch     e.g. you may want to blanket disable PetscDevice init (and disable say Kokkos init), but
355*a4af0ceeSJacob Faibussowitsch     have all CUDA devices still initialize.
356*a4af0ceeSJacob Faibussowitsch 
357*a4af0ceeSJacob Faibussowitsch  All told the following happens:
358*a4af0ceeSJacob Faibussowitsch  0. defaultInitType -> LAZY
359*a4af0ceeSJacob Faibussowitsch  1. Check for log_view/log_summary, if yes defaultInitType -> EAGER
360*a4af0ceeSJacob Faibussowitsch  2. PetscDevice initializes each sub type with deviceDefaultInitType.
361*a4af0ceeSJacob Faibussowitsch  2.1 Each enabled PetscDevice sub-type then does the above disable or view check in addition
362*a4af0ceeSJacob Faibussowitsch      to checking for specific device init. if view or specific device init
363*a4af0ceeSJacob Faibussowitsch      subTypeDefaultInitType -> EAGER. disabled once again overrides all.
364*a4af0ceeSJacob Faibussowitsch  */
365*a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceInitializeFromOptions_Internal(MPI_Comm comm)
366030f984aSJacob Faibussowitsch {
367*a4af0ceeSJacob Faibussowitsch   PetscBool           flg,defaultView = PETSC_FALSE,initializeDeviceContextEagerly = PETSC_FALSE;
368*a4af0ceeSJacob Faibussowitsch   PetscInt            defaultDevice   = PETSC_DECIDE;
369*a4af0ceeSJacob Faibussowitsch   PetscDeviceType     deviceContextInitDevice = PETSC_DEVICE_DEFAULT;
370*a4af0ceeSJacob Faibussowitsch   PetscDeviceInitType defaultInitType;
371*a4af0ceeSJacob Faibussowitsch   PetscErrorCode      ierr;
372*a4af0ceeSJacob Faibussowitsch 
373*a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
374*a4af0ceeSJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
375*a4af0ceeSJacob Faibussowitsch     int result;
376*a4af0ceeSJacob Faibussowitsch 
377*a4af0ceeSJacob Faibussowitsch     ierr = MPI_Comm_compare(comm,PETSC_COMM_WORLD,&result);CHKERRMPI(ierr);
378*a4af0ceeSJacob Faibussowitsch     /* in order to accurately assign ranks to gpus we need to get the MPI_Comm_rank of the
379*a4af0ceeSJacob Faibussowitsch      * global space */
380*a4af0ceeSJacob Faibussowitsch     if (PetscUnlikely(result != MPI_IDENT)) {
381*a4af0ceeSJacob Faibussowitsch       char name[MPI_MAX_OBJECT_NAME] = {};
382*a4af0ceeSJacob Faibussowitsch       int  len; /* unused */
383*a4af0ceeSJacob Faibussowitsch 
384*a4af0ceeSJacob Faibussowitsch       ierr = MPI_Comm_get_name(comm,name,&len);CHKERRMPI(ierr);
385*a4af0ceeSJacob Faibussowitsch       SETERRQ1(comm,PETSC_ERR_MPI,"Default devices being initialized on MPI_Comm '%s' not PETSC_COMM_WORLD",name);
386*a4af0ceeSJacob Faibussowitsch     }
387*a4af0ceeSJacob Faibussowitsch   }
388*a4af0ceeSJacob Faibussowitsch   comm = PETSC_COMM_WORLD; /* from this point on we assume we're on PETSC_COMM_WORLD */
389*a4af0ceeSJacob Faibussowitsch   ierr = PetscRegisterFinalize(PetscDeviceFinalize_Private);CHKERRQ(ierr);
390*a4af0ceeSJacob Faibussowitsch   ierr = PetscOptionsHasName(PETSC_NULLPTR,PETSC_NULLPTR,"-log_view",&flg);CHKERRQ(ierr);
391*a4af0ceeSJacob Faibussowitsch   if (!flg) {
392*a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsHasName(PETSC_NULLPTR,PETSC_NULLPTR,"-log_summary",&flg);CHKERRQ(ierr);
393*a4af0ceeSJacob Faibussowitsch   }
394*a4af0ceeSJacob Faibussowitsch   {
395*a4af0ceeSJacob Faibussowitsch     PetscInt initIdx = flg ? PETSC_DEVICE_INIT_EAGER : PETSC_DEVICE_INIT_LAZY;
396*a4af0ceeSJacob Faibussowitsch 
397*a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsBegin(comm,PETSC_NULLPTR,"PetscDevice Options","Sys");CHKERRQ(ierr);
398*a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsEList("-device_enable","How (or whether) to initialize PetscDevices","PetscDeviceInitializeFromOptions_Internal()",PetscDeviceInitTypes,3,PetscDeviceInitTypes[initIdx],&initIdx,PETSC_NULLPTR);CHKERRQ(ierr);
399*a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsRangeInt("-device_select","Which device to use. Pass " PetscStringize(PETSC_DECIDE) " to have PETSc decide or (given they exist) [0-NUM_DEVICE) for a specific device","PetscDeviceCreate",defaultDevice,&defaultDevice,PETSC_NULLPTR,PETSC_DECIDE,std::numeric_limits<int>::max());CHKERRQ(ierr);
400*a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsBool("-device_view","Display device information and assignments (forces eager initialization)",PETSC_NULLPTR,defaultView,&defaultView,&flg);CHKERRQ(ierr);
401*a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsEnd();CHKERRQ(ierr);
402*a4af0ceeSJacob Faibussowitsch     if (initIdx == PETSC_DEVICE_INIT_NONE) {
403*a4af0ceeSJacob Faibussowitsch       /* disabled all device initialization if devices are globally disabled */
404*a4af0ceeSJacob Faibussowitsch       if (PetscUnlikely(defaultDevice != PETSC_DECIDE)) SETERRQ(comm,PETSC_ERR_USER_INPUT,"You have disabled devices but also specified a particular device to use, these options are mutually exlusive");
405*a4af0ceeSJacob Faibussowitsch       defaultView = PETSC_FALSE;
406*a4af0ceeSJacob Faibussowitsch     } else {
407*a4af0ceeSJacob Faibussowitsch       defaultView = static_cast<decltype(defaultView)>(defaultView && flg);
408*a4af0ceeSJacob Faibussowitsch       if (defaultView) initIdx = PETSC_DEVICE_INIT_EAGER;
409*a4af0ceeSJacob Faibussowitsch     }
410*a4af0ceeSJacob Faibussowitsch     defaultInitType = static_cast<decltype(defaultInitType)>(initIdx);
411*a4af0ceeSJacob Faibussowitsch   }
412*a4af0ceeSJacob Faibussowitsch   static_assert((PETSC_DEVICE_INVALID == 0) && (PETSC_DEVICE_MAX < std::numeric_limits<int>::max()),"");
413*a4af0ceeSJacob Faibussowitsch   for (int i = 1; i < PETSC_DEVICE_MAX; ++i) {
414*a4af0ceeSJacob Faibussowitsch     const auto deviceType = static_cast<PetscDeviceType>(i);
415*a4af0ceeSJacob Faibussowitsch     auto initType         = defaultInitType;
416*a4af0ceeSJacob Faibussowitsch 
417*a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceInitializeTypeFromOptions_Private(comm,deviceType,defaultDevice,defaultView,&initType);CHKERRQ(ierr);
418*a4af0ceeSJacob Faibussowitsch     if (PetscDeviceConfiguredFor_Internal(deviceType) && (initType == PETSC_DEVICE_INIT_EAGER)) {
419*a4af0ceeSJacob Faibussowitsch       initializeDeviceContextEagerly = PETSC_TRUE;
420*a4af0ceeSJacob Faibussowitsch       deviceContextInitDevice        = deviceType;
421*a4af0ceeSJacob Faibussowitsch     }
422*a4af0ceeSJacob Faibussowitsch   }
423*a4af0ceeSJacob Faibussowitsch   if (initializeDeviceContextEagerly) {
424*a4af0ceeSJacob Faibussowitsch     PetscDeviceContext dctx;
425*a4af0ceeSJacob Faibussowitsch 
426*a4af0ceeSJacob Faibussowitsch     /* somewhat inefficient here as the device context is potentially fully set up twice (once
427*a4af0ceeSJacob Faibussowitsch      * when retrieved then the second time if setfromoptions makes changes) */
428*a4af0ceeSJacob Faibussowitsch     ierr = PetscInfo1(PETSC_NULLPTR,"Eagerly initializing PetscDeviceContext with %s device\n",PetscDeviceTypes[deviceContextInitDevice]);CHKERRQ(ierr);
429*a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceContextSetRootDeviceType_Internal(deviceContextInitDevice);CHKERRQ(ierr);
430*a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceContextGetCurrentContext(&dctx);CHKERRQ(ierr);
431*a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceContextSetFromOptions(comm,"root_",dctx);CHKERRQ(ierr);
432*a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceContextSetUp(dctx);CHKERRQ(ierr);
433*a4af0ceeSJacob Faibussowitsch   }
434*a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
435*a4af0ceeSJacob Faibussowitsch }
436*a4af0ceeSJacob Faibussowitsch 
437*a4af0ceeSJacob Faibussowitsch /* Get the default PetscDevice for a particular type and constructs them if lazily initialized. */
438*a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceGetDefaultForType_Internal(PetscDeviceType type, PetscDevice *device)
439*a4af0ceeSJacob Faibussowitsch {
440*a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
441*a4af0ceeSJacob Faibussowitsch 
442*a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
443*a4af0ceeSJacob Faibussowitsch   PetscValidPointer(device,2);
444*a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceInitialize(type);CHKERRQ(ierr);
445*a4af0ceeSJacob Faibussowitsch   *device = defaultDevices[type];
446*a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
447030f984aSJacob Faibussowitsch }
448