1 /* 2 Code that allows a user to dictate what malloc() PETSc uses. 3 */ 4 #include <petscsys.h> /*I "petscsys.h" I*/ 5 #include <stdarg.h> 6 #if defined(PETSC_HAVE_MALLOC_H) 7 #include <malloc.h> 8 #endif 9 #if defined(PETSC_HAVE_MEMKIND) 10 #include <errno.h> 11 #include <memkind.h> 12 typedef enum {PETSC_MK_DEFAULT=0,PETSC_MK_HBW_PREFERRED=1} PetscMemkindType; 13 PetscMemkindType currentmktype = PETSC_MK_HBW_PREFERRED; 14 PetscMemkindType previousmktype = PETSC_MK_HBW_PREFERRED; 15 #endif 16 /* 17 We want to make sure that all mallocs of double or complex numbers are complex aligned. 18 1) on systems with memalign() we call that routine to get an aligned memory location 19 2) on systems without memalign() we 20 - allocate one sizeof(PetscScalar) extra space 21 - we shift the pointer up slightly if needed to get PetscScalar aligned 22 - if shifted we store at ptr[-1] the amount of shift (plus a classid) 23 */ 24 #define SHIFT_CLASSID 456123 25 26 PETSC_EXTERN PetscErrorCode PetscMallocAlign(size_t mem,PetscBool clear,int line,const char func[],const char file[],void **result) 27 { 28 if (!mem) {*result = NULL; return 0;} 29 #if PetscDefined(HAVE_MEMKIND) 30 { 31 int err; 32 33 err = memkind_posix_memalign(currentmktype ? MEMKIND_HBW_PREFERRED : MEMKIND_DEFAULT,result,PETSC_MEMALIGN,mem); 34 PetscCheck(err != EINVAL,PETSC_COMM_SELF,PETSC_ERR_MEM,"Memkind: invalid 3rd or 4th argument of memkind_posix_memalign()"); 35 if (err == ENOMEM) PetscInfo(NULL,"Memkind: fail to request HBW memory %.0f, falling back to normal memory\n",(PetscLogDouble)mem); 36 PetscCheck(*result,PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 37 if (clear) PetscCall(PetscMemzero(*result,mem)); 38 } 39 #else /* PetscDefined(HAVE_MEMKIND) */ 40 # if PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8) 41 if (clear) *result = calloc(1+mem/sizeof(int),sizeof(int)); 42 else *result = malloc(mem); 43 44 PetscCheck(*result,PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 45 if (PetscLogMemory) PetscCall(PetscMemzero(*result,mem)); 46 47 # elif PetscDefined(HAVE_MEMALIGN) 48 *result = memalign(PETSC_MEMALIGN,mem); 49 PetscCheck(*result,PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 50 if (clear || PetscLogMemory) PetscCall(PetscMemzero(*result,mem)); 51 # else /* PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) || PetscDefined(HAVE_MEMALIGN) */ 52 { 53 int *ptr,shift; 54 /* 55 malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned 56 */ 57 if (clear) { 58 ptr = (int*)calloc(1+(mem + 2*PETSC_MEMALIGN)/sizeof(int),sizeof(int)); 59 } else { 60 ptr = (int*)malloc(mem + 2*PETSC_MEMALIGN); 61 } 62 PetscCheck(ptr,PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 63 shift = (int)(((PETSC_UINTPTR_T) ptr) % PETSC_MEMALIGN); 64 shift = (2*PETSC_MEMALIGN - shift)/sizeof(int); 65 ptr[shift-1] = shift + SHIFT_CLASSID; 66 ptr += shift; 67 *result = (void*)ptr; 68 if (PetscLogMemory) PetscCall(PetscMemzero(*result,mem)); 69 } 70 # endif /* PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) || PetscDefined(HAVE_MEMALIGN) */ 71 #endif /* PetscDefined(HAVE_MEMKIND) */ 72 return 0; 73 } 74 75 PETSC_EXTERN PetscErrorCode PetscFreeAlign(void *ptr,int line,const char func[],const char file[]) 76 { 77 if (!ptr) return 0; 78 #if PetscDefined(HAVE_MEMKIND) 79 memkind_free(0,ptr); /* specify the kind to 0 so that memkind will look up for the right type */ 80 #else /* PetscDefined(HAVE_MEMKIND) */ 81 # if (!(PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !PetscDefined(HAVE_MEMALIGN)) 82 { 83 /* 84 Previous int tells us how many ints the pointer has been shifted from 85 the original address provided by the system malloc(). 86 */ 87 const int shift = *(((int*)ptr)-1) - SHIFT_CLASSID; 88 89 PetscCheck(shift <= PETSC_MEMALIGN-1,PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap"); 90 PetscCheck(shift >= 0,PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap"); 91 ptr = (void*)(((int*)ptr) - shift); 92 } 93 # endif 94 95 # if PetscDefined(HAVE_FREE_RETURN_INT) 96 int err = free(ptr); 97 PetscCheck(!err,PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"System free returned error %d\n",err); 98 # else 99 free(ptr); 100 # endif 101 #endif 102 return 0; 103 } 104 105 PETSC_EXTERN PetscErrorCode PetscReallocAlign(size_t mem, int line, const char func[], const char file[], void **result) 106 { 107 if (!mem) { 108 PetscCall(PetscFreeAlign(*result, line, func, file)); 109 *result = NULL; 110 return 0; 111 } 112 #if PetscDefined(HAVE_MEMKIND) 113 *result = memkind_realloc(currentmktype ? MEMKIND_HBW_PREFERRED : MEMKIND_DEFAULT,*result,mem); 114 #else 115 # if (!(PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !PetscDefined(HAVE_MEMALIGN)) 116 { 117 /* 118 Previous int tells us how many ints the pointer has been shifted from 119 the original address provided by the system malloc(). 120 */ 121 int shift = *(((int*)*result)-1) - SHIFT_CLASSID; 122 PetscCheck(shift <= PETSC_MEMALIGN-1,PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap"); 123 PetscCheck(shift >= 0,PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap"); 124 *result = (void*)(((int*)*result) - shift); 125 } 126 # endif 127 128 # if (PetscDefined(HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) || PetscDefined(HAVE_MEMALIGN) 129 *result = realloc(*result, mem); 130 # else 131 { 132 /* 133 malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned 134 */ 135 int *ptr = (int*)realloc(*result,mem + 2*PETSC_MEMALIGN); 136 if (ptr) { 137 int shift = (int)(((PETSC_UINTPTR_T) ptr) % PETSC_MEMALIGN); 138 shift = (2*PETSC_MEMALIGN - shift)/sizeof(int); 139 ptr[shift-1] = shift + SHIFT_CLASSID; 140 ptr += shift; 141 *result = (void*)ptr; 142 } else { 143 *result = NULL; 144 } 145 } 146 # endif 147 #endif 148 PetscCheck(*result,PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 149 #if PetscDefined(HAVE_MEMALIGN) 150 /* There are no standard guarantees that realloc() maintains the alignment of memalign(), so I think we have to 151 * realloc and, if the alignment is wrong, malloc/copy/free. */ 152 if (((size_t) (*result)) % PETSC_MEMALIGN) { 153 void *newResult; 154 # if PetscDefined(HAVE_MEMKIND) 155 { 156 int err; 157 err = memkind_posix_memalign(currentmktype ? MEMKIND_HBW_PREFERRED : MEMKIND_DEFAULT,&newResult,PETSC_MEMALIGN,mem); 158 PetscCheck(err != EINVAL,PETSC_COMM_SELF,PETSC_ERR_MEM,"Memkind: invalid 3rd or 4th argument of memkind_posix_memalign()"); 159 if (err == ENOMEM) PetscInfo(NULL,"Memkind: fail to request HBW memory %.0f, falling back to normal memory\n",(PetscLogDouble)mem); 160 } 161 # else 162 newResult = memalign(PETSC_MEMALIGN,mem); 163 # endif 164 PetscCheck(newResult,PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 165 PetscCall(PetscMemcpy(newResult,*result,mem)); 166 # if PetscDefined(HAVE_FREE_RETURN_INT) 167 { 168 int err = free(*result); 169 PetscCheck(!err,PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"System free returned error %d\n",err); 170 } 171 # else 172 # if defined(PETSC_HAVE_MEMKIND) 173 memkind_free(0,*result); 174 # else 175 free(*result); 176 # endif 177 # endif 178 *result = newResult; 179 } 180 #endif 181 return 0; 182 } 183 184 PetscErrorCode (*PetscTrMalloc)(size_t,PetscBool,int,const char[],const char[],void**) = PetscMallocAlign; 185 PetscErrorCode (*PetscTrFree)(void*,int,const char[],const char[]) = PetscFreeAlign; 186 PetscErrorCode (*PetscTrRealloc)(size_t,int,const char[],const char[],void**) = PetscReallocAlign; 187 188 PETSC_INTERN PetscBool petscsetmallocvisited; 189 PetscBool petscsetmallocvisited = PETSC_FALSE; 190 191 /*@C 192 PetscMallocSet - Sets the routines used to do mallocs and frees. 193 This routine MUST be called before PetscInitialize() and may be 194 called only once. 195 196 Not Collective 197 198 Input Parameters: 199 + imalloc - the routine that provides the malloc (also provides calloc(), which is used depends on the second argument) 200 . ifree - the routine that provides the free 201 - iralloc - the routine that provides the realloc 202 203 Level: developer 204 205 @*/ 206 PetscErrorCode PetscMallocSet(PetscErrorCode (*imalloc)(size_t,PetscBool,int,const char[],const char[],void**), 207 PetscErrorCode (*ifree)(void*,int,const char[],const char[]), 208 PetscErrorCode (*iralloc)(size_t, int, const char[], const char[], void **)) 209 { 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 { 236 PetscFunctionBegin; 237 PetscTrMalloc = PetscMallocAlign; 238 PetscTrFree = PetscFreeAlign; 239 PetscTrRealloc = PetscReallocAlign; 240 petscsetmallocvisited = PETSC_FALSE; 241 PetscFunctionReturn(0); 242 } 243 244 PetscErrorCode PetscMemoryTrace(const char label[]) 245 { 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 Notes: 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 { 281 PetscFunctionBegin; 282 if (PetscTrMalloc == PetscMallocAlign) { 283 #if defined(PETSC_HAVE_MEMKIND) 284 previousmktype = currentmktype; 285 currentmktype = PETSC_MK_DEFAULT; 286 #endif 287 } else { 288 /* Save the previous choice */ 289 PetscTrMallocOld = PetscTrMalloc; 290 PetscTrReallocOld = PetscTrRealloc; 291 PetscTrFreeOld = PetscTrFree; 292 PetscTrMalloc = PetscMallocAlign; 293 PetscTrFree = PetscFreeAlign; 294 PetscTrRealloc = PetscReallocAlign; 295 } 296 PetscFunctionReturn(0); 297 } 298 299 /*@C 300 PetscMallocResetDRAM - Reset the changes made by PetscMallocSetDRAM 301 302 Not Collective 303 304 Level: developer 305 306 .seealso: `PetscMallocSetDRAM()` 307 @*/ 308 PetscErrorCode PetscMallocResetDRAM(void) 309 { 310 PetscFunctionBegin; 311 if (PetscTrMalloc == PetscMallocAlign) { 312 #if defined(PETSC_HAVE_MEMKIND) 313 currentmktype = previousmktype; 314 #endif 315 } else { 316 /* Reset to the previous choice */ 317 PetscTrMalloc = PetscTrMallocOld; 318 PetscTrRealloc = PetscTrReallocOld; 319 PetscTrFree = PetscTrFreeOld; 320 } 321 PetscFunctionReturn(0); 322 } 323 324 static PetscBool petscmalloccoalesce = 325 #if defined(PETSC_USE_MALLOC_COALESCED) 326 PETSC_TRUE; 327 #else 328 PETSC_FALSE; 329 #endif 330 331 /*@C 332 PetscMallocSetCoalesce - Use coalesced malloc when allocating groups of objects 333 334 Not Collective 335 336 Input Parameters: 337 . coalesce - PETSC_TRUE to use coalesced malloc for multi-object allocation. 338 339 Options Database Keys: 340 . -malloc_coalesce - turn coalesced malloc on or off 341 342 Note: 343 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. 344 This function can only be called immediately after PetscInitialize() 345 346 Level: developer 347 348 .seealso: `PetscMallocA()` 349 @*/ 350 PetscErrorCode PetscMallocSetCoalesce(PetscBool coalesce) 351 { 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 { 382 va_list Argp; 383 size_t bytes[8],sumbytes; 384 void **ptr[8]; 385 int i; 386 387 PetscFunctionBegin; 388 PetscCheck(n <= 8,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Attempt to allocate %d objects but only 8 supported",n); 389 bytes[0] = bytes0; 390 ptr[0] = (void**)ptr0; 391 sumbytes = (bytes0 + PETSC_MEMALIGN-1) & ~(PETSC_MEMALIGN-1); 392 va_start(Argp,ptr0); 393 for (i=1; i<n; i++) { 394 bytes[i] = va_arg(Argp,size_t); 395 ptr[i] = va_arg(Argp,void**); 396 sumbytes += (bytes[i] + PETSC_MEMALIGN-1) & ~(PETSC_MEMALIGN-1); 397 } 398 va_end(Argp); 399 if (petscmalloccoalesce) { 400 char *p; 401 PetscCall((*PetscTrMalloc)(sumbytes,clear,lineno,function,filename,(void**)&p)); 402 if (p == NULL) { 403 for (i=0; i<n; i++) { 404 *ptr[i] = NULL; 405 } 406 } else { 407 for (i=0; i<n; i++) { 408 *ptr[i] = bytes[i] ? p : NULL; 409 p = (char*)PetscAddrAlign(p + bytes[i]); 410 } 411 } 412 } else { 413 for (i=0; i<n; i++) { 414 PetscCall((*PetscTrMalloc)(bytes[i],clear,lineno,function,filename,(void**)ptr[i])); 415 } 416 } 417 PetscFunctionReturn(0); 418 } 419 420 /*@C 421 PetscFreeA - Free one or more objects, possibly allocated using coalesced malloc 422 423 Not Collective 424 425 Input Parameters: 426 + n - number of objects to free (at least 1) 427 . lineno - line number to attribute deallocation (typically __LINE__) 428 . function - function to attribute deallocation (typically PETSC_FUNCTION_NAME) 429 . filename - file name to attribute deallocation (typically __FILE__) 430 - ptr0 ... - first of n pointers to free 431 432 Note: 433 This function is not normally called directly by users, but rather via the macros PetscFree(), PetscFree2(), etc. 434 435 The pointers are zeroed to prevent users from accidentally reusing space that has been freed. 436 437 Level: developer 438 439 .seealso: `PetscMallocAlign()`, `PetscMallocSet()`, `PetscMallocA()`, `PetscFree1()`, `PetscFree2()`, `PetscFree3()`, `PetscFree4()`, `PetscFree5()`, `PetscFree6()`, `PetscFree7()` 440 @*/ 441 PetscErrorCode PetscFreeA(int n,int lineno,const char *function,const char *filename,void *ptr0,...) 442 { 443 va_list Argp; 444 void **ptr[8]; 445 int i; 446 447 PetscFunctionBegin; 448 PetscCheck(n <= 8,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Attempt to allocate %d objects but only up to 8 supported",n); 449 ptr[0] = (void**)ptr0; 450 va_start(Argp,ptr0); 451 for (i=1; i<n; i++) { 452 ptr[i] = va_arg(Argp,void**); 453 } 454 va_end(Argp); 455 if (petscmalloccoalesce) { 456 for (i=0; i<n; i++) { /* Find first nonempty allocation */ 457 if (*ptr[i]) break; 458 } 459 while (--n > i) { 460 *ptr[n] = NULL; 461 } 462 PetscCall((*PetscTrFree)(*ptr[n],lineno,function,filename)); 463 *ptr[n] = NULL; 464 } else { 465 while (--n >= 0) { 466 PetscCall((*PetscTrFree)(*ptr[n],lineno,function,filename)); 467 *ptr[n] = NULL; 468 } 469 } 470 PetscFunctionReturn(0); 471 } 472