1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at 2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights 3 // reserved. See files LICENSE and NOTICE for details. 4 // 5 // This file is part of CEED, a collection of benchmarks, miniapps, software 6 // libraries and APIs for efficient high-order finite element and spectral 7 // element discretizations for exascale applications. For more information and 8 // source code availability see http://github.com/ceed. 9 // 10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 11 // a collaborative effort of two U.S. Department of Energy organizations (Office 12 // of Science and the National Nuclear Security Administration) responsible for 13 // the planning and preparation of a capable exascale ecosystem, including 14 // software, applications, hardware, advanced system engineering and early 15 // testbed platforms, in support of the nation's exascale computing imperative. 16 17 #define _POSIX_C_SOURCE 200112 18 #include <ceed-impl.h> 19 #include <ceed-backend.h> 20 #include <limits.h> 21 #include <stdarg.h> 22 #include <stddef.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 /// @cond DOXYGEN_SKIP 28 static CeedRequest ceed_request_immediate; 29 static CeedRequest ceed_request_ordered; 30 31 static struct { 32 char prefix[CEED_MAX_RESOURCE_LEN]; 33 int (*init)(const char *resource, Ceed f); 34 unsigned int priority; 35 } backends[32]; 36 static size_t num_backends; 37 38 #define CEED_FTABLE_ENTRY(class, method) \ 39 {#class #method, offsetof(struct class ##_private, method)} 40 /// @endcond 41 42 /// @file 43 /// Implementation of core components of Ceed library 44 /// 45 /// @addtogroup Ceed 46 /// @{ 47 48 /** 49 @brief Request immediate completion 50 51 This predefined constant is passed as the \ref CeedRequest argument to 52 interfaces when the caller wishes for the operation to be performed 53 immediately. The code 54 55 @code 56 CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE); 57 @endcode 58 59 is semantically equivalent to 60 61 @code 62 CeedRequest request; 63 CeedOperatorApply(op, ..., &request); 64 CeedRequestWait(&request); 65 @endcode 66 67 @sa CEED_REQUEST_ORDERED 68 **/ 69 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate; 70 71 /** 72 @brief Request ordered completion 73 74 This predefined constant is passed as the \ref CeedRequest argument to 75 interfaces when the caller wishes for the operation to be completed in the 76 order that it is submitted to the device. It is typically used in a construct 77 such as 78 79 @code 80 CeedRequest request; 81 CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED); 82 CeedOperatorApply(op2, ..., &request); 83 // other optional work 84 CeedWait(&request); 85 @endcode 86 87 which allows the sequence to complete asynchronously but does not start 88 `op2` until `op1` has completed. 89 90 @fixme The current implementation is overly strict, offering equivalent 91 semantics to CEED_REQUEST_IMMEDIATE. 92 93 @sa CEED_REQUEST_IMMEDIATE 94 */ 95 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered; 96 97 /** 98 @brief Error handling implementation; use \ref CeedError instead. 99 100 @ref Developer 101 **/ 102 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, 103 int ecode, const char *format, ...) { 104 va_list args; 105 int retval; 106 va_start(args, format); 107 if (ceed) { 108 retval = ceed->Error(ceed, filename, lineno, func, ecode, format, args); 109 } else { 110 // This function doesn't actually return 111 retval = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args); 112 } 113 va_end(args); 114 return retval; 115 } 116 117 /** 118 @brief Error handler that returns without printing anything. 119 120 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 121 122 @ref Developer 123 **/ 124 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno, 125 const char *func, int ecode, const char *format, 126 va_list args) { 127 return ecode; 128 } 129 130 /** 131 @brief Error handler that prints to stderr and aborts 132 133 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 134 135 @ref Developer 136 **/ 137 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno, 138 const char *func, int ecode, 139 const char *format, va_list args) { 140 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 141 vfprintf(stderr, format, args); 142 fprintf(stderr, "\n"); 143 abort(); 144 return ecode; 145 } 146 147 /** 148 @brief Error handler that prints to stderr and exits 149 150 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 151 152 In contrast to CeedErrorAbort(), this exits without a signal, so atexit() 153 handlers (e.g., as used by gcov) are run. 154 155 @ref Developer 156 **/ 157 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, 158 const char *func, int ecode, 159 const char *format, va_list args) { 160 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 161 vfprintf(stderr, format, args); 162 fprintf(stderr, "\n"); 163 exit(ecode); 164 return ecode; 165 } 166 167 /** 168 @brief Set error handler 169 170 A default error handler is set in CeedInit(). Use this function to change 171 the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined 172 error handler. 173 174 @ref Developer 175 **/ 176 int CeedSetErrorHandler(Ceed ceed, 177 int (eh)(Ceed, const char *, int, const char *, 178 int, const char *, va_list)) { 179 ceed->Error = eh; 180 return 0; 181 } 182 183 /** 184 @brief Register a Ceed backend 185 186 @param prefix Prefix of resources for this backend to respond to. For 187 example, the reference backend responds to "/cpu/self". 188 @param init Initialization function called by CeedInit() when the backend 189 is selected to drive the requested resource. 190 @param priority Integer priority. Lower values are preferred in case the 191 resource requested by CeedInit() has non-unique best prefix 192 match. 193 194 @return An error code: 0 - success, otherwise - failure 195 196 @ref Advanced 197 **/ 198 int CeedRegister(const char *prefix, 199 int (*init)(const char *, Ceed), unsigned int priority) { 200 if (num_backends >= sizeof(backends) / sizeof(backends[0])) { 201 return CeedError(NULL, 1, "Too many backends"); 202 } 203 strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN); 204 backends[num_backends].init = init; 205 backends[num_backends].priority = priority; 206 num_backends++; 207 return 0; 208 } 209 210 /** 211 @brief Allocate an array on the host; use CeedMalloc() 212 213 Memory usage can be tracked by the library. This ensures sufficient 214 alignment for vectorization and should be used for large allocations. 215 216 @param n Number of units to allocate 217 @param unit Size of each unit 218 @param p Address of pointer to hold the result. 219 220 @return An error code: 0 - success, otherwise - failure 221 222 @sa CeedFree() 223 224 @ref Advanced 225 **/ 226 int CeedMallocArray(size_t n, size_t unit, void *p) { 227 int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit); 228 if (ierr) 229 return CeedError(NULL, ierr, 230 "posix_memalign failed to allocate %zd members of size %zd\n", n, unit); 231 return 0; 232 } 233 234 /** 235 @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc() 236 237 Memory usage can be tracked by the library. 238 239 @param n Number of units to allocate 240 @param unit Size of each unit 241 @param p Address of pointer to hold the result. 242 243 @return An error code: 0 - success, otherwise - failure 244 245 @sa CeedFree() 246 247 @ref Advanced 248 **/ 249 int CeedCallocArray(size_t n, size_t unit, void *p) { 250 *(void **)p = calloc(n, unit); 251 if (n && unit && !*(void **)p) 252 return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n", 253 n, unit); 254 return 0; 255 } 256 257 /** 258 @brief Reallocate an array on the host; use CeedRealloc() 259 260 Memory usage can be tracked by the library. 261 262 @param n Number of units to allocate 263 @param unit Size of each unit 264 @param p Address of pointer to hold the result. 265 266 @return An error code: 0 - success, otherwise - failure 267 268 @sa CeedFree() 269 270 @ref Advanced 271 **/ 272 int CeedReallocArray(size_t n, size_t unit, void *p) { 273 *(void **)p = realloc(*(void **)p, n*unit); 274 if (n && unit && !*(void **)p) 275 return CeedError(NULL, 1, 276 "realloc failed to allocate %zd members of size %zd\n", 277 n, unit); 278 return 0; 279 } 280 281 /// Free memory allocated using CeedMalloc() or CeedCalloc() 282 /// 283 /// @param p address of pointer to memory. This argument is of type void* to 284 /// avoid needing a cast, but is the address of the pointer (which is zeroed) 285 /// rather than the pointer. 286 int CeedFree(void *p) { 287 free(*(void **)p); 288 *(void **)p = NULL; 289 return 0; 290 } 291 292 /** 293 @brief Wait for a CeedRequest to complete. 294 295 Calling CeedRequestWait on a NULL request is a no-op. 296 297 @param req Address of CeedRequest to wait for; zeroed on completion. 298 299 @return An error code: 0 - success, otherwise - failure 300 301 @ref Advanced 302 **/ 303 int CeedRequestWait(CeedRequest *req) { 304 if (!*req) return 0; 305 return CeedError(NULL, 2, "CeedRequestWait not implemented"); 306 } 307 308 /** 309 @brief Initialize a \ref Ceed to use the specified resource. 310 311 @param resource Resource to use, e.g., "/cpu/self" 312 @param ceed The library context 313 @sa CeedRegister() CeedDestroy() 314 315 @return An error code: 0 - success, otherwise - failure 316 317 @ref Basic 318 **/ 319 int CeedInit(const char *resource, Ceed *ceed) { 320 int ierr; 321 size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority; 322 323 // Find matching backend 324 if (!resource) return CeedError(NULL, 1, "No resource provided"); 325 for (size_t i=0; i<num_backends; i++) { 326 size_t n; 327 const char *prefix = backends[i].prefix; 328 for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {} 329 priority = backends[i].priority; 330 if (n > matchlen || (n == matchlen && matchpriority > priority)) { 331 matchlen = n; 332 matchpriority = priority; 333 matchidx = i; 334 } 335 } 336 if (!matchlen) return CeedError(NULL, 1, "No suitable backend"); 337 338 // Setup Ceed 339 ierr = CeedCalloc(1,ceed); CeedChk(ierr); 340 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 341 if (!ceed_error_handler) ceed_error_handler = "abort"; 342 if (!strcmp(ceed_error_handler, "exit")) 343 (*ceed)->Error = CeedErrorExit; 344 else 345 (*ceed)->Error = CeedErrorAbort; 346 (*ceed)->refcount = 1; 347 (*ceed)->data = NULL; 348 349 // Set lookup table 350 foffset foffsets[] = { 351 CEED_FTABLE_ENTRY(Ceed, Error), 352 CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType), 353 CEED_FTABLE_ENTRY(Ceed, Destroy), 354 CEED_FTABLE_ENTRY(Ceed, VectorCreate), 355 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate), 356 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked), 357 CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1), 358 CEED_FTABLE_ENTRY(Ceed, BasisCreateH1), 359 CEED_FTABLE_ENTRY(Ceed, TensorContractCreate), 360 CEED_FTABLE_ENTRY(Ceed, QFunctionCreate), 361 CEED_FTABLE_ENTRY(Ceed, OperatorCreate), 362 CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate), 363 CEED_FTABLE_ENTRY(CeedVector, SetArray), 364 CEED_FTABLE_ENTRY(CeedVector, SetValue), 365 CEED_FTABLE_ENTRY(CeedVector, GetArray), 366 CEED_FTABLE_ENTRY(CeedVector, GetArrayRead), 367 CEED_FTABLE_ENTRY(CeedVector, RestoreArray), 368 CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead), 369 CEED_FTABLE_ENTRY(CeedVector, Destroy), 370 CEED_FTABLE_ENTRY(CeedElemRestriction, Apply), 371 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock), 372 CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy), 373 CEED_FTABLE_ENTRY(CeedBasis, Apply), 374 CEED_FTABLE_ENTRY(CeedBasis, Destroy), 375 CEED_FTABLE_ENTRY(CeedTensorContract, Apply), 376 CEED_FTABLE_ENTRY(CeedTensorContract, Destroy), 377 CEED_FTABLE_ENTRY(CeedQFunction, Apply), 378 CEED_FTABLE_ENTRY(CeedQFunction, Destroy), 379 CEED_FTABLE_ENTRY(CeedOperator, Apply), 380 CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian), 381 CEED_FTABLE_ENTRY(CeedOperator, Destroy), 382 {NULL, 0} // End of lookup table - used in SetBackendFunction loop 383 }; 384 385 ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr); 386 memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets)); 387 388 // Backend specific setup 389 ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr); 390 391 return 0; 392 } 393 394 /** 395 @brief Retrieve a parent CEED 396 397 @param ceed Ceed to retrieve parent of 398 @param[out] parent Address to save the parent to 399 400 @return An error code: 0 - success, otherwise - failure 401 402 @ref Developer 403 **/ 404 int CeedGetParent(Ceed ceed, Ceed *parent) { 405 int ierr; 406 if (ceed->parent) { 407 ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr); 408 return 0; 409 } 410 *parent = ceed; 411 return 0; 412 } 413 414 /** 415 @brief Retrieve a delegate CEED 416 417 @param ceed Ceed to retrieve delegate of 418 @param[out] delegate Address to save the delegate to 419 420 @return An error code: 0 - success, otherwise - failure 421 422 @ref Developer 423 **/ 424 int CeedGetDelegate(Ceed ceed, Ceed *delegate) { 425 *delegate = ceed->delegate; 426 return 0; 427 } 428 429 /** 430 @brief Set a delegate CEED 431 432 This function allows a CEED to set a delegate CEED. All backend 433 implementations default to the delegate CEED, unless overridden. 434 435 @param ceed Ceed to set delegate of 436 @param[out] delegate Address to set the delegate to 437 438 @return An error code: 0 - success, otherwise - failure 439 440 @ref Advanced 441 **/ 442 int CeedSetDelegate(Ceed ceed, Ceed delegate) { 443 ceed->delegate = delegate; 444 delegate->parent = ceed; 445 return 0; 446 } 447 448 /** 449 @brief Retrieve a delegate CEED for a specific object type 450 451 @param ceed Ceed to retrieve delegate of 452 @param[out] delegate Address to save the delegate to 453 454 @return An error code: 0 - success, otherwise - failure 455 456 @ref Developer 457 **/ 458 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) { 459 CeedInt ierr; 460 461 // Check for object delegate 462 for (CeedInt i=0; i<ceed->objdelegatecount; i++) { 463 if (!strcmp(objname, ceed->objdelegates->objname)) { 464 *delegate = ceed->objdelegates->delegate; 465 return 0; 466 } 467 } 468 469 // Use default delegate if no object delegate 470 ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr); 471 472 return 0; 473 } 474 475 /** 476 @brief Set a delegate CEED for a specific object type 477 478 This function allows a CEED to set a delegate CEED for a given type of 479 CEED object. All backend implementations default to the delegate CEED for 480 this object. For example, 481 CeedSetObjectDelegate(ceed, refceed, "Basis") 482 uses refceed implementations for all CeedBasis backend functions. 483 484 @param ceed Ceed to set delegate of 485 @param[out] delegate Address to set the delegate to 486 487 @return An error code: 0 - success, otherwise - failure 488 489 @ref Advanced 490 **/ 491 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) { 492 CeedInt ierr; 493 CeedInt count = ceed->objdelegatecount; 494 495 // Malloc or Realloc 496 if (count) { 497 ierr = CeedRealloc(count+1, &ceed->objdelegates); 498 CeedChk(ierr); 499 } else { 500 ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr); 501 } 502 ceed->objdelegatecount++; 503 504 // Set object delegate 505 ceed->objdelegates[count].delegate = delegate; 506 ierr = CeedCalloc(strlen(objname)+1, &ceed->objdelegates[count].objname); 507 CeedChk(ierr); 508 strncpy(ceed->objdelegates[count].objname, objname, strlen(objname)+1); 509 510 // Set delegate parent 511 delegate->parent = ceed; 512 513 return 0; 514 } 515 516 /** 517 @brief Return Ceed perferred memory type 518 519 @param ceed Ceed to get preferred memory type of 520 @param[out] delegate Address to save preferred memory type to 521 522 @return An error code: 0 - success, otherwise - failure 523 524 @ref Basic 525 **/ 526 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) { 527 int ierr; 528 529 if (ceed->GetPreferredMemType) { 530 ierr = ceed->GetPreferredMemType(type); CeedChk(ierr); 531 } else { 532 Ceed delegate; 533 ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr); 534 535 if (delegate) { 536 ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr); 537 } else { 538 *type = CEED_MEM_HOST; 539 } 540 } 541 542 return 0; 543 } 544 545 /** 546 @brief Set a backend function 547 548 This function is used for a backend to set the function associated with 549 the CEED objects. For example, 550 CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate) 551 sets the backend implementation of 'CeedVectorCreate' and 552 CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply) 553 sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed' 554 is not required for the object type ("Basis" vs "CeedBasis"). 555 556 @param ceed Ceed for error handling 557 @param type Type of Ceed object to set function for 558 @param[out] object Ceed object to set function for 559 @param fname Name of function to set 560 @param f Function to set 561 562 @return An error code: 0 - success, otherwise - failure 563 564 @ref Advanced 565 **/ 566 int CeedSetBackendFunction(Ceed ceed, 567 const char *type, void *object, 568 const char *fname, int (*f)()) { 569 char lookupname[CEED_MAX_RESOURCE_LEN+1] = ""; 570 571 // Build lookup name 572 if (strcmp(type, "Ceed")) 573 strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN); 574 strncat(lookupname, type, CEED_MAX_RESOURCE_LEN); 575 strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN); 576 577 // Find and use offset 578 for (CeedInt i = 0; ceed->foffsets[i].fname; i++) { 579 if (!strcmp(ceed->foffsets[i].fname, lookupname)) { 580 size_t offset = ceed->foffsets[i].offset; 581 int (**fpointer)(void) = (int (**)(void))((char*)object + offset); 582 *fpointer = f; 583 return 0; 584 } 585 } 586 587 return CeedError(ceed, 1, 588 "Requested function '%s' was not found for CEED object '%s'", fname, type); 589 } 590 591 /** 592 @brief Retrieve backend data for a CEED 593 594 @param ceed Ceed to retrieve data of 595 @param[out] data Address to save data to 596 597 @return An error code: 0 - success, otherwise - failure 598 599 @ref Advanced 600 **/ 601 int CeedGetData(Ceed ceed, void* *data) { 602 *data = ceed->data; 603 return 0; 604 } 605 606 /** 607 @brief Set backend data for a CEED 608 609 @param ceed Ceed to set data of 610 @param data Address of data to set 611 612 @return An error code: 0 - success, otherwise - failure 613 614 @ref Advanced 615 **/ 616 int CeedSetData(Ceed ceed, void* *data) { 617 ceed->data = *data; 618 return 0; 619 } 620 621 /** 622 @brief Destroy a Ceed context 623 624 @param ceed Address of Ceed context to destroy 625 626 @return An error code: 0 - success, otherwise - failure 627 628 @ref Basic 629 **/ 630 int CeedDestroy(Ceed *ceed) { 631 int ierr; 632 633 if (!*ceed || --(*ceed)->refcount > 0) return 0; 634 if ((*ceed)->delegate) { 635 ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr); 636 } 637 if ((*ceed)->objdelegatecount > 0) { 638 for (int i=0; i<(*ceed)->objdelegatecount; i++) { 639 ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr); 640 ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr); 641 } 642 ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr); 643 } 644 if ((*ceed)->Destroy) { 645 ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 646 } 647 ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr); 648 ierr = CeedFree(ceed); CeedChk(ierr); 649 return 0; 650 } 651 652 /// @} 653