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