1 /* 2 Code that allows a user to dictate what malloc() PETSc uses. 3 */ 4 #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for posix_memalign() */ 5 #include <petscsys.h> /*I "petscsys.h" I*/ 6 #include <stdarg.h> 7 #if defined(PETSC_HAVE_MALLOC_H) 8 #include <malloc.h> 9 #endif 10 #if defined(PETSC_HAVE_MEMKIND) 11 #include <errno.h> 12 #include <memkind.h> 13 typedef enum { 14 PETSC_MK_DEFAULT = 0, 15 PETSC_MK_HBW_PREFERRED = 1 16 } PetscMemkindType; 17 PetscMemkindType currentmktype = PETSC_MK_HBW_PREFERRED; 18 PetscMemkindType previousmktype = PETSC_MK_HBW_PREFERRED; 19 #endif 20 /* 21 We want to make sure that all mallocs of double or complex numbers are complex aligned. 22 1) on systems with memalign() we call that routine to get an aligned memory location 23 2) on systems without memalign() we 24 - allocate one sizeof(PetscScalar) extra space 25 - we shift the pointer up slightly if needed to get PetscScalar aligned 26 - if shifted we store at ptr[-1] the amount of shift (plus a classid) 27 */ 28 #define SHIFT_CLASSID 456123 29 30 PETSC_EXTERN PetscErrorCode PetscMallocAlign(size_t mem, PetscBool clear, int line, const char func[], const char file[], void **result) 31 { 32 if (!mem) { 33 *result = NULL; 34 return 0; 35 } 36 #if PetscDefined(HAVE_MEMKIND) 37 { 38 int err; 39 40 err = memkind_posix_memalign(currentmktype ? MEMKIND_HBW_PREFERRED : MEMKIND_DEFAULT, result, PETSC_MEMALIGN, mem); 41 PetscCheck(err != EINVAL, PETSC_COMM_SELF, PETSC_ERR_MEM, "Memkind: invalid 3rd or 4th argument of memkind_posix_memalign()"); 42 if (err == ENOMEM) PetscInfo(NULL, "Memkind: fail to request HBW memory %.0f, falling back to normal memory\n", (PetscLogDouble)mem); 43 PetscCheck(*result, PETSC_COMM_SELF, line, func, file, PETSC_ERR_MEM, PETSC_ERROR_INITIAL, "Memory requested %.0f", (PetscLogDouble)mem); 44 if (clear) PetscCall(PetscMemzero(*result, mem)); 45 } 46 #else /* PetscDefined(HAVE_MEMKIND) */ 47 #if PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8) 48 if (clear) *result = calloc(1 + mem / sizeof(int), sizeof(int)); 49 else *result = malloc(mem); 50 51 PetscCheck(*result, PETSC_COMM_SELF, line, func, file, PETSC_ERR_MEM, PETSC_ERROR_INITIAL, "Memory requested %.0f", (PetscLogDouble)mem); 52 if (PetscLogMemory) PetscCall(PetscMemzero(*result, mem)); 53 #elif PetscDefined(HAVE_POSIX_MEMALIGN) 54 int ret = posix_memalign(result, PETSC_MEMALIGN, mem); 55 PetscCheck(ret == 0, PETSC_COMM_SELF, line, func, file, PETSC_ERR_MEM, PETSC_ERROR_INITIAL, "Memory requested %.0f", (PetscLogDouble)mem); 56 if (clear || PetscLogMemory) PetscCall(PetscMemzero(*result, mem)); 57 #else /* PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) || PetscDefined(HAVE_POSIX_MEMALIGN) */ 58 { 59 int *ptr, shift; 60 /* 61 malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned 62 */ 63 if (clear) { 64 ptr = (int *)calloc(1 + (mem + 2 * PETSC_MEMALIGN) / sizeof(int), sizeof(int)); 65 } else { 66 ptr = (int *)malloc(mem + 2 * PETSC_MEMALIGN); 67 } 68 PetscCheck(ptr, PETSC_COMM_SELF, line, func, file, PETSC_ERR_MEM, PETSC_ERROR_INITIAL, "Memory requested %.0f", (PetscLogDouble)mem); 69 shift = (int)(((PETSC_UINTPTR_T)ptr) % PETSC_MEMALIGN); 70 shift = (2 * PETSC_MEMALIGN - shift) / sizeof(int); 71 ptr[shift - 1] = shift + SHIFT_CLASSID; 72 ptr += shift; 73 *result = (void *)ptr; 74 if (PetscLogMemory) PetscCall(PetscMemzero(*result, mem)); 75 } 76 #endif /* PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) || PetscDefined(HAVE_POSIX_MEMALIGN) */ 77 #endif /* PetscDefined(HAVE_MEMKIND) */ 78 return 0; 79 } 80 81 PETSC_EXTERN PetscErrorCode PetscFreeAlign(void *ptr, int line, const char func[], const char file[]) 82 { 83 if (!ptr) return 0; 84 #if PetscDefined(HAVE_MEMKIND) 85 memkind_free(0, ptr); /* specify the kind to 0 so that memkind will look up for the right type */ 86 #else /* PetscDefined(HAVE_MEMKIND) */ 87 #if (!(PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !PetscDefined(HAVE_POSIX_MEMALIGN)) 88 { 89 /* 90 Previous int tells us how many ints the pointer has been shifted from 91 the original address provided by the system malloc(). 92 */ 93 const int shift = *(((int *)ptr) - 1) - SHIFT_CLASSID; 94 95 PetscCheck(shift <= PETSC_MEMALIGN - 1, PETSC_COMM_SELF, line, func, file, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, "Likely memory corruption in heap"); 96 PetscCheck(shift >= 0, PETSC_COMM_SELF, line, func, file, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, "Likely memory corruption in heap"); 97 ptr = (void *)(((int *)ptr) - shift); 98 } 99 #endif 100 101 #if PetscDefined(HAVE_FREE_RETURN_INT) 102 int err = free(ptr); 103 PetscCheck(!err, PETSC_COMM_SELF, line, func, file, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, "System free returned error %d\n", err); 104 #else 105 free(ptr); 106 #endif 107 #endif 108 return 0; 109 } 110 111 PETSC_EXTERN PetscErrorCode PetscReallocAlign(size_t mem, int line, const char func[], const char file[], void **result) 112 { 113 if (!mem) { 114 PetscCall(PetscFreeAlign(*result, line, func, file)); 115 *result = NULL; 116 return 0; 117 } 118 #if PetscDefined(HAVE_MEMKIND) 119 *result = memkind_realloc(currentmktype ? MEMKIND_HBW_PREFERRED : MEMKIND_DEFAULT, *result, mem); 120 #else 121 #if (!(PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !PetscDefined(HAVE_POSIX_MEMALIGN)) 122 { 123 /* 124 Previous int tells us how many ints the pointer has been shifted from 125 the original address provided by the system malloc(). 126 */ 127 int shift = *(((int *)*result) - 1) - SHIFT_CLASSID; 128 PetscCheck(shift <= PETSC_MEMALIGN - 1, PETSC_COMM_SELF, line, func, file, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, "Likely memory corruption in heap"); 129 PetscCheck(shift >= 0, PETSC_COMM_SELF, line, func, file, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, "Likely memory corruption in heap"); 130 *result = (void *)(((int *)*result) - shift); 131 } 132 #endif 133 134 #if (PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) || PetscDefined(HAVE_POSIX_MEMALIGN) 135 *result = realloc(*result, mem); 136 #else 137 { 138 /* 139 malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned 140 */ 141 int *ptr = (int *)realloc(*result, mem + 2 * PETSC_MEMALIGN); 142 if (ptr) { 143 int shift = (int)(((PETSC_UINTPTR_T)ptr) % PETSC_MEMALIGN); 144 shift = (2 * PETSC_MEMALIGN - shift) / sizeof(int); 145 ptr[shift - 1] = shift + SHIFT_CLASSID; 146 ptr += shift; 147 *result = (void *)ptr; 148 } else { 149 *result = NULL; 150 } 151 } 152 #endif 153 #endif 154 PetscCheck(*result, PETSC_COMM_SELF, line, func, file, PETSC_ERR_MEM, PETSC_ERROR_INITIAL, "Memory requested %.0f", (PetscLogDouble)mem); 155 #if PetscDefined(HAVE_POSIX_MEMALIGN) 156 /* There are no standard guarantees that realloc() maintains the alignment of memalign(), so I think we have to 157 * realloc and, if the alignment is wrong, malloc/copy/free. */ 158 if (((size_t)(*result)) % PETSC_MEMALIGN) { 159 void *newResult; 160 #if PetscDefined(HAVE_MEMKIND) 161 { 162 int err; 163 err = memkind_posix_memalign(currentmktype ? MEMKIND_HBW_PREFERRED : MEMKIND_DEFAULT, &newResult, PETSC_MEMALIGN, mem); 164 PetscCheck(err != EINVAL, PETSC_COMM_SELF, PETSC_ERR_MEM, "Memkind: invalid 3rd or 4th argument of memkind_posix_memalign()"); 165 if (err == ENOMEM) PetscInfo(NULL, "Memkind: fail to request HBW memory %.0f, falling back to normal memory\n", (PetscLogDouble)mem); 166 } 167 PetscCheck(newResult, PETSC_COMM_SELF, line, func, file, PETSC_ERR_MEM, PETSC_ERROR_INITIAL, "Memory requested %.0f", (PetscLogDouble)mem); 168 #else 169 PetscCheck(posix_memalign(&newResult, PETSC_MEMALIGN, mem) == 0, PETSC_COMM_SELF, line, func, file, PETSC_ERR_MEM, PETSC_ERROR_INITIAL, "Memory requested %.0f", (PetscLogDouble)mem); 170 #endif 171 PetscCall(PetscMemcpy(newResult, *result, mem)); 172 #if PetscDefined(HAVE_FREE_RETURN_INT) 173 { 174 int err = free(*result); 175 PetscCheck(!err, PETSC_COMM_SELF, line, func, file, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, "System free returned error %d\n", err); 176 } 177 #else 178 #if defined(PETSC_HAVE_MEMKIND) 179 memkind_free(0, *result); 180 #else 181 free(*result); 182 #endif 183 #endif 184 *result = newResult; 185 } 186 #endif 187 return 0; 188 } 189 190 PetscErrorCode (*PetscTrMalloc)(size_t, PetscBool, int, const char[], const char[], void **) = PetscMallocAlign; 191 PetscErrorCode (*PetscTrFree)(void *, int, const char[], const char[]) = PetscFreeAlign; 192 PetscErrorCode (*PetscTrRealloc)(size_t, int, const char[], const char[], void **) = PetscReallocAlign; 193 194 PETSC_INTERN PetscBool petscsetmallocvisited; 195 PetscBool petscsetmallocvisited = PETSC_FALSE; 196 197 /*@C 198 PetscMallocSet - Sets the routines used to do mallocs and frees. 199 This routine MUST be called before `PetscInitialize()` and may be 200 called only once. 201 202 Not Collective 203 204 Input Parameters: 205 + imalloc - the routine that provides the malloc (also provides calloc(), which is used depends on the second argument) 206 . ifree - the routine that provides the free 207 - iralloc - the routine that provides the realloc 208 209 Level: developer 210 211 .seealso: `PetscMallocClear()` 212 @*/ 213 PetscErrorCode PetscMallocSet(PetscErrorCode (*imalloc)(size_t, PetscBool, int, const char[], const char[], void **), PetscErrorCode (*ifree)(void *, int, const char[], const char[]), PetscErrorCode (*iralloc)(size_t, int, const char[], const char[], void **)) 214 { 215 PetscFunctionBegin; 216 PetscCheck(!petscsetmallocvisited || !(imalloc != PetscTrMalloc || ifree != PetscTrFree), PETSC_COMM_SELF, PETSC_ERR_SUP, "cannot call multiple times"); 217 PetscTrMalloc = imalloc; 218 PetscTrFree = ifree; 219 PetscTrRealloc = iralloc; 220 petscsetmallocvisited = PETSC_TRUE; 221 PetscFunctionReturn(0); 222 } 223 224 /*@C 225 PetscMallocClear - Resets the routines used to do mallocs and frees to the defaults. 226 227 Not Collective 228 229 Level: developer 230 231 Note: 232 In general one should never run a PETSc program with different malloc() and 233 free() settings for different parts; this is because one NEVER wants to 234 free() an address that was malloced by a different memory management system 235 236 Called in `PetscFinalize()` so that if `PetscInitialize()` is called again it starts with a fresh slate of allocation information 237 238 .seealso: `PetscMallocSet` 239 @*/ 240 PetscErrorCode PetscMallocClear(void) 241 { 242 PetscFunctionBegin; 243 PetscTrMalloc = PetscMallocAlign; 244 PetscTrFree = PetscFreeAlign; 245 PetscTrRealloc = PetscReallocAlign; 246 petscsetmallocvisited = PETSC_FALSE; 247 PetscFunctionReturn(0); 248 } 249 250 PetscErrorCode PetscMemoryTrace(const char label[]) 251 { 252 PetscLogDouble mem, mal; 253 static PetscLogDouble oldmem = 0, oldmal = 0; 254 255 PetscFunctionBegin; 256 PetscCall(PetscMemoryGetCurrentUsage(&mem)); 257 PetscCall(PetscMallocGetCurrentUsage(&mal)); 258 259 PetscCall(PetscPrintf(PETSC_COMM_WORLD, "%s High water %8.3f MB increase %8.3f MB Current %8.3f MB increase %8.3f MB\n", label, mem * 1e-6, (mem - oldmem) * 1e-6, mal * 1e-6, (mal - oldmal) * 1e-6)); 260 oldmem = mem; 261 oldmal = mal; 262 PetscFunctionReturn(0); 263 } 264 265 static PetscErrorCode (*PetscTrMallocOld)(size_t, PetscBool, int, const char[], const char[], void **) = PetscMallocAlign; 266 static PetscErrorCode (*PetscTrReallocOld)(size_t, int, const char[], const char[], void **) = PetscReallocAlign; 267 static PetscErrorCode (*PetscTrFreeOld)(void *, int, const char[], const char[]) = PetscFreeAlign; 268 269 /*@C 270 PetscMallocSetDRAM - Set `PetscMalloc()` to use DRAM. 271 If memkind is available, change the memkind type. Otherwise, switch the 272 current malloc and free routines to the `PetscMallocAlign()` and 273 `PetscFreeAlign()` (PETSc default). 274 275 Not Collective 276 277 Level: developer 278 279 Note: 280 This provides a way to do the allocation on DRAM temporarily. One 281 can switch back to the previous choice by calling `PetscMallocReset()`. 282 283 .seealso: `PetscMallocReset()` 284 @*/ 285 PetscErrorCode PetscMallocSetDRAM(void) 286 { 287 PetscFunctionBegin; 288 if (PetscTrMalloc == PetscMallocAlign) { 289 #if defined(PETSC_HAVE_MEMKIND) 290 previousmktype = currentmktype; 291 currentmktype = PETSC_MK_DEFAULT; 292 #endif 293 } else { 294 /* Save the previous choice */ 295 PetscTrMallocOld = PetscTrMalloc; 296 PetscTrReallocOld = PetscTrRealloc; 297 PetscTrFreeOld = PetscTrFree; 298 PetscTrMalloc = PetscMallocAlign; 299 PetscTrFree = PetscFreeAlign; 300 PetscTrRealloc = PetscReallocAlign; 301 } 302 PetscFunctionReturn(0); 303 } 304 305 /*@C 306 PetscMallocResetDRAM - Reset the changes made by `PetscMallocSetDRAM()` 307 308 Not Collective 309 310 Level: developer 311 312 .seealso: `PetscMallocSetDRAM()` 313 @*/ 314 PetscErrorCode PetscMallocResetDRAM(void) 315 { 316 PetscFunctionBegin; 317 if (PetscTrMalloc == PetscMallocAlign) { 318 #if defined(PETSC_HAVE_MEMKIND) 319 currentmktype = previousmktype; 320 #endif 321 } else { 322 /* Reset to the previous choice */ 323 PetscTrMalloc = PetscTrMallocOld; 324 PetscTrRealloc = PetscTrReallocOld; 325 PetscTrFree = PetscTrFreeOld; 326 } 327 PetscFunctionReturn(0); 328 } 329 330 static PetscBool petscmalloccoalesce = 331 #if defined(PETSC_USE_MALLOC_COALESCED) 332 PETSC_TRUE; 333 #else 334 PETSC_FALSE; 335 #endif 336 337 /*@C 338 PetscMallocSetCoalesce - Use coalesced malloc when allocating groups of objects 339 340 Not Collective 341 342 Input Parameters: 343 . coalesce - `PETSC_TRUE` to use coalesced malloc for multi-object allocation. 344 345 Options Database Keys: 346 . -malloc_coalesce - turn coalesced malloc on or off 347 348 Notes: 349 PETSc uses coalesced malloc by default for optimized builds and not for debugging builds. 350 351 This default can be changed via the command-line option -malloc_coalesce or by calling this function. 352 353 This function can only be called immediately after `PetscInitialize()` 354 355 Level: developer 356 357 .seealso: `PetscMallocA()` 358 @*/ 359 PetscErrorCode PetscMallocSetCoalesce(PetscBool coalesce) 360 { 361 PetscFunctionBegin; 362 petscmalloccoalesce = coalesce; 363 PetscFunctionReturn(0); 364 } 365 366 /*@C 367 PetscMallocA - Allocate and optionally clear one or more objects, possibly using coalesced malloc 368 369 Not Collective 370 371 Input Parameters: 372 + n - number of objects to allocate (at least 1) 373 . clear - use calloc() to allocate space initialized to zero 374 . lineno - line number to attribute allocation (typically __LINE__) 375 . function - function to attribute allocation (typically PETSC_FUNCTION_NAME) 376 . filename - file name to attribute allocation (typically __FILE__) 377 - bytes0 - first of n object sizes 378 379 Output Parameters: 380 . ptr0 - first of n pointers to allocate 381 382 Notes 383 This function is not normally called directly by users, but rather via the macros `PetscMalloc1()`, `PetscMalloc2()`, or `PetscCalloc1()`, etc. 384 385 Level: developer 386 387 .seealso: `PetscMallocAlign()`, `PetscMallocSet()`, `PetscMalloc1()`, `PetscMalloc2()`, `PetscMalloc3()`, `PetscMalloc4()`, `PetscMalloc5()`, `PetscMalloc6()`, `PetscMalloc7()`, `PetscCalloc1()`, `PetscCalloc2()`, `PetscCalloc3()`, `PetscCalloc4()`, `PetscCalloc5()`, `PetscCalloc6()`, `PetscCalloc7()`, `PetscFreeA()` 388 @*/ 389 PetscErrorCode PetscMallocA(int n, PetscBool clear, int lineno, const char *function, const char *filename, size_t bytes0, void *ptr0, ...) 390 { 391 va_list Argp; 392 size_t bytes[8], sumbytes; 393 void **ptr[8]; 394 int i; 395 396 PetscFunctionBegin; 397 PetscCheck(n <= 8, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Attempt to allocate %d objects but only 8 supported", n); 398 bytes[0] = bytes0; 399 ptr[0] = (void **)ptr0; 400 sumbytes = (bytes0 + PETSC_MEMALIGN - 1) & ~(PETSC_MEMALIGN - 1); 401 va_start(Argp, ptr0); 402 for (i = 1; i < n; i++) { 403 bytes[i] = va_arg(Argp, size_t); 404 ptr[i] = va_arg(Argp, void **); 405 sumbytes += (bytes[i] + PETSC_MEMALIGN - 1) & ~(PETSC_MEMALIGN - 1); 406 } 407 va_end(Argp); 408 if (petscmalloccoalesce) { 409 char *p; 410 PetscCall((*PetscTrMalloc)(sumbytes, clear, lineno, function, filename, (void **)&p)); 411 if (p == NULL) { 412 for (i = 0; i < n; i++) *ptr[i] = NULL; 413 } else { 414 for (i = 0; i < n; i++) { 415 *ptr[i] = bytes[i] ? p : NULL; 416 p = (char *)PetscAddrAlign(p + bytes[i]); 417 } 418 } 419 } else { 420 for (i = 0; i < n; i++) PetscCall((*PetscTrMalloc)(bytes[i], clear, lineno, function, filename, (void **)ptr[i])); 421 } 422 PetscFunctionReturn(0); 423 } 424 425 /*@C 426 PetscFreeA - Free one or more objects, possibly allocated using coalesced malloc 427 428 Not Collective 429 430 Input Parameters: 431 + n - number of objects to free (at least 1) 432 . lineno - line number to attribute deallocation (typically __LINE__) 433 . function - function to attribute deallocation (typically PETSC_FUNCTION_NAME) 434 . filename - file name to attribute deallocation (typically __FILE__) 435 - ptr0 ... - first of n pointers to free 436 437 Notes: 438 This function is not normally called directly by users, but rather via the macros `PetscFree()`, `PetscFree2()`, etc. 439 440 The pointers are zeroed to prevent users from accidentally reusing space that has been freed. 441 442 Level: developer 443 444 .seealso: `PetscMallocAlign()`, `PetscMallocSet()`, `PetscMallocA()`, `PetscFree1()`, `PetscFree2()`, `PetscFree3()`, `PetscFree4()`, `PetscFree5()`, `PetscFree6()`, `PetscFree7()` 445 @*/ 446 PetscErrorCode PetscFreeA(int n, int lineno, const char *function, const char *filename, void *ptr0, ...) 447 { 448 va_list Argp; 449 void **ptr[8]; 450 int i; 451 452 PetscFunctionBegin; 453 PetscCheck(n <= 8, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Attempt to allocate %d objects but only up to 8 supported", n); 454 ptr[0] = (void **)ptr0; 455 va_start(Argp, ptr0); 456 for (i = 1; i < n; i++) ptr[i] = va_arg(Argp, void **); 457 va_end(Argp); 458 if (petscmalloccoalesce) { 459 for (i = 0; i < n; i++) { /* Find first nonempty allocation */ 460 if (*ptr[i]) break; 461 } 462 while (--n > i) *ptr[n] = NULL; 463 PetscCall((*PetscTrFree)(*ptr[n], lineno, function, filename)); 464 *ptr[n] = NULL; 465 } else { 466 while (--n >= 0) { 467 PetscCall((*PetscTrFree)(*ptr[n], lineno, function, filename)); 468 *ptr[n] = NULL; 469 } 470 } 471 PetscFunctionReturn(0); 472 } 473