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