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 @*/ 209 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 **)) { 210 PetscFunctionBegin; 211 PetscCheck(!petscsetmallocvisited || !(imalloc != PetscTrMalloc || ifree != PetscTrFree), PETSC_COMM_SELF, PETSC_ERR_SUP, "cannot call multiple times"); 212 PetscTrMalloc = imalloc; 213 PetscTrFree = ifree; 214 PetscTrRealloc = iralloc; 215 petscsetmallocvisited = PETSC_TRUE; 216 PetscFunctionReturn(0); 217 } 218 219 /*@C 220 PetscMallocClear - Resets the routines used to do mallocs and frees to the defaults. 221 222 Not Collective 223 224 Level: developer 225 226 Notes: 227 In general one should never run a PETSc program with different malloc() and 228 free() settings for different parts; this is because one NEVER wants to 229 free() an address that was malloced by a different memory management system 230 231 Called in PetscFinalize() so that if PetscInitialize() is called again it starts with a fresh slate of allocation information 232 233 @*/ 234 PetscErrorCode PetscMallocClear(void) { 235 PetscFunctionBegin; 236 PetscTrMalloc = PetscMallocAlign; 237 PetscTrFree = PetscFreeAlign; 238 PetscTrRealloc = PetscReallocAlign; 239 petscsetmallocvisited = PETSC_FALSE; 240 PetscFunctionReturn(0); 241 } 242 243 PetscErrorCode PetscMemoryTrace(const char label[]) { 244 PetscLogDouble mem, mal; 245 static PetscLogDouble oldmem = 0, oldmal = 0; 246 247 PetscFunctionBegin; 248 PetscCall(PetscMemoryGetCurrentUsage(&mem)); 249 PetscCall(PetscMallocGetCurrentUsage(&mal)); 250 251 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)); 252 oldmem = mem; 253 oldmal = mal; 254 PetscFunctionReturn(0); 255 } 256 257 static PetscErrorCode (*PetscTrMallocOld)(size_t, PetscBool, int, const char[], const char[], void **) = PetscMallocAlign; 258 static PetscErrorCode (*PetscTrReallocOld)(size_t, int, const char[], const char[], void **) = PetscReallocAlign; 259 static PetscErrorCode (*PetscTrFreeOld)(void *, int, const char[], const char[]) = PetscFreeAlign; 260 261 /*@C 262 PetscMallocSetDRAM - Set PetscMalloc to use DRAM. 263 If memkind is available, change the memkind type. Otherwise, switch the 264 current malloc and free routines to the PetscMallocAlign and 265 PetscFreeAlign (PETSc default). 266 267 Not Collective 268 269 Level: developer 270 271 Notes: 272 This provides a way to do the allocation on DRAM temporarily. One 273 can switch back to the previous choice by calling PetscMallocReset(). 274 275 .seealso: `PetscMallocReset()` 276 @*/ 277 PetscErrorCode PetscMallocSetDRAM(void) { 278 PetscFunctionBegin; 279 if (PetscTrMalloc == PetscMallocAlign) { 280 #if defined(PETSC_HAVE_MEMKIND) 281 previousmktype = currentmktype; 282 currentmktype = PETSC_MK_DEFAULT; 283 #endif 284 } else { 285 /* Save the previous choice */ 286 PetscTrMallocOld = PetscTrMalloc; 287 PetscTrReallocOld = PetscTrRealloc; 288 PetscTrFreeOld = PetscTrFree; 289 PetscTrMalloc = PetscMallocAlign; 290 PetscTrFree = PetscFreeAlign; 291 PetscTrRealloc = PetscReallocAlign; 292 } 293 PetscFunctionReturn(0); 294 } 295 296 /*@C 297 PetscMallocResetDRAM - Reset the changes made by PetscMallocSetDRAM 298 299 Not Collective 300 301 Level: developer 302 303 .seealso: `PetscMallocSetDRAM()` 304 @*/ 305 PetscErrorCode PetscMallocResetDRAM(void) { 306 PetscFunctionBegin; 307 if (PetscTrMalloc == PetscMallocAlign) { 308 #if defined(PETSC_HAVE_MEMKIND) 309 currentmktype = previousmktype; 310 #endif 311 } else { 312 /* Reset to the previous choice */ 313 PetscTrMalloc = PetscTrMallocOld; 314 PetscTrRealloc = PetscTrReallocOld; 315 PetscTrFree = PetscTrFreeOld; 316 } 317 PetscFunctionReturn(0); 318 } 319 320 static PetscBool petscmalloccoalesce = 321 #if defined(PETSC_USE_MALLOC_COALESCED) 322 PETSC_TRUE; 323 #else 324 PETSC_FALSE; 325 #endif 326 327 /*@C 328 PetscMallocSetCoalesce - Use coalesced malloc when allocating groups of objects 329 330 Not Collective 331 332 Input Parameters: 333 . coalesce - PETSC_TRUE to use coalesced malloc for multi-object allocation. 334 335 Options Database Keys: 336 . -malloc_coalesce - turn coalesced malloc on or off 337 338 Note: 339 PETSc uses coalesced malloc by default for optimized builds and not for debugging builds. This default can be changed via the command-line option -malloc_coalesce or by calling this function. 340 This function can only be called immediately after PetscInitialize() 341 342 Level: developer 343 344 .seealso: `PetscMallocA()` 345 @*/ 346 PetscErrorCode PetscMallocSetCoalesce(PetscBool coalesce) { 347 PetscFunctionBegin; 348 petscmalloccoalesce = coalesce; 349 PetscFunctionReturn(0); 350 } 351 352 /*@C 353 PetscMallocA - Allocate and optionally clear one or more objects, possibly using coalesced malloc 354 355 Not Collective 356 357 Input Parameters: 358 + n - number of objects to allocate (at least 1) 359 . clear - use calloc() to allocate space initialized to zero 360 . lineno - line number to attribute allocation (typically __LINE__) 361 . function - function to attribute allocation (typically PETSC_FUNCTION_NAME) 362 . filename - file name to attribute allocation (typically __FILE__) 363 - bytes0 - first of n object sizes 364 365 Output Parameters: 366 . ptr0 - first of n pointers to allocate 367 368 Notes: 369 This function is not normally called directly by users, but rather via the macros PetscMalloc1(), PetscMalloc2(), or PetscCalloc1(), etc. 370 371 Level: developer 372 373 .seealso: `PetscMallocAlign()`, `PetscMallocSet()`, `PetscMalloc1()`, `PetscMalloc2()`, `PetscMalloc3()`, `PetscMalloc4()`, `PetscMalloc5()`, `PetscMalloc6()`, `PetscMalloc7()`, `PetscCalloc1()`, `PetscCalloc2()`, `PetscCalloc3()`, `PetscCalloc4()`, `PetscCalloc5()`, `PetscCalloc6()`, `PetscCalloc7()`, `PetscFreeA()` 374 @*/ 375 PetscErrorCode PetscMallocA(int n, PetscBool clear, int lineno, const char *function, const char *filename, size_t bytes0, void *ptr0, ...) { 376 va_list Argp; 377 size_t bytes[8], sumbytes; 378 void **ptr[8]; 379 int i; 380 381 PetscFunctionBegin; 382 PetscCheck(n <= 8, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Attempt to allocate %d objects but only 8 supported", n); 383 bytes[0] = bytes0; 384 ptr[0] = (void **)ptr0; 385 sumbytes = (bytes0 + PETSC_MEMALIGN - 1) & ~(PETSC_MEMALIGN - 1); 386 va_start(Argp, ptr0); 387 for (i = 1; i < n; i++) { 388 bytes[i] = va_arg(Argp, size_t); 389 ptr[i] = va_arg(Argp, void **); 390 sumbytes += (bytes[i] + PETSC_MEMALIGN - 1) & ~(PETSC_MEMALIGN - 1); 391 } 392 va_end(Argp); 393 if (petscmalloccoalesce) { 394 char *p; 395 PetscCall((*PetscTrMalloc)(sumbytes, clear, lineno, function, filename, (void **)&p)); 396 if (p == NULL) { 397 for (i = 0; i < n; i++) *ptr[i] = NULL; 398 } else { 399 for (i = 0; i < n; i++) { 400 *ptr[i] = bytes[i] ? p : NULL; 401 p = (char *)PetscAddrAlign(p + bytes[i]); 402 } 403 } 404 } else { 405 for (i = 0; i < n; i++) PetscCall((*PetscTrMalloc)(bytes[i], clear, lineno, function, filename, (void **)ptr[i])); 406 } 407 PetscFunctionReturn(0); 408 } 409 410 /*@C 411 PetscFreeA - Free one or more objects, possibly allocated using coalesced malloc 412 413 Not Collective 414 415 Input Parameters: 416 + n - number of objects to free (at least 1) 417 . lineno - line number to attribute deallocation (typically __LINE__) 418 . function - function to attribute deallocation (typically PETSC_FUNCTION_NAME) 419 . filename - file name to attribute deallocation (typically __FILE__) 420 - ptr0 ... - first of n pointers to free 421 422 Note: 423 This function is not normally called directly by users, but rather via the macros PetscFree(), PetscFree2(), etc. 424 425 The pointers are zeroed to prevent users from accidentally reusing space that has been freed. 426 427 Level: developer 428 429 .seealso: `PetscMallocAlign()`, `PetscMallocSet()`, `PetscMallocA()`, `PetscFree1()`, `PetscFree2()`, `PetscFree3()`, `PetscFree4()`, `PetscFree5()`, `PetscFree6()`, `PetscFree7()` 430 @*/ 431 PetscErrorCode PetscFreeA(int n, int lineno, const char *function, const char *filename, void *ptr0, ...) { 432 va_list Argp; 433 void **ptr[8]; 434 int i; 435 436 PetscFunctionBegin; 437 PetscCheck(n <= 8, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Attempt to allocate %d objects but only up to 8 supported", n); 438 ptr[0] = (void **)ptr0; 439 va_start(Argp, ptr0); 440 for (i = 1; i < n; i++) ptr[i] = va_arg(Argp, void **); 441 va_end(Argp); 442 if (petscmalloccoalesce) { 443 for (i = 0; i < n; i++) { /* Find first nonempty allocation */ 444 if (*ptr[i]) break; 445 } 446 while (--n > i) *ptr[n] = NULL; 447 PetscCall((*PetscTrFree)(*ptr[n], lineno, function, filename)); 448 *ptr[n] = NULL; 449 } else { 450 while (--n >= 0) { 451 PetscCall((*PetscTrFree)(*ptr[n], lineno, function, filename)); 452 *ptr[n] = NULL; 453 } 454 } 455 PetscFunctionReturn(0); 456 } 457