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