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 30 if (!mem) {*result = NULL; return 0;} 31 #if defined(PETSC_HAVE_MEMKIND) 32 { 33 if (!currentmktype) ierr = memkind_posix_memalign(MEMKIND_DEFAULT,result,PETSC_MEMALIGN,mem); 34 else ierr = memkind_posix_memalign(MEMKIND_HBW_PREFERRED,result,PETSC_MEMALIGN,mem); 35 if (ierr == EINVAL) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_MEM,"Memkind: invalid 3rd or 4th argument of memkind_posix_memalign()"); 36 if (ierr == ENOMEM) PetscInfo1(0,"Memkind: fail to request HBW memory %.0f, falling back to normal memory\n",(PetscLogDouble)mem); 37 if (clear) {ierr = PetscMemzero(*result,mem);CHKERRQ(ierr);} 38 } 39 #else 40 # if defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8) 41 if (clear) { 42 *result = calloc(1+mem/sizeof(int),sizeof(int)); 43 } else { 44 *result = malloc(mem); 45 } 46 if (PetscLogMemory) {ierr = PetscMemzero(*result,mem);CHKERRQ(ierr);} 47 48 # elif defined(PETSC_HAVE_MEMALIGN) 49 *result = memalign(PETSC_MEMALIGN,mem); 50 if (clear || PetscLogMemory) { 51 ierr = PetscMemzero(*result,mem);CHKERRQ(ierr); 52 } 53 # else 54 { 55 int *ptr; 56 /* 57 malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned 58 */ 59 if (clear) { 60 ptr = (int*)calloc(1+(mem + 2*PETSC_MEMALIGN)/sizeof(int),sizeof(int)); 61 } else { 62 ptr = (int*)malloc(mem + 2*PETSC_MEMALIGN); 63 } 64 if (ptr) { 65 int shift = (int)(((PETSC_UINTPTR_T) ptr) % PETSC_MEMALIGN); 66 shift = (2*PETSC_MEMALIGN - shift)/sizeof(int); 67 ptr[shift-1] = shift + SHIFT_CLASSID; 68 ptr += shift; 69 *result = (void*)ptr; 70 if (PetscLogMemory) {ierr = PetscMemzero(*result,mem);CHKERRQ(ierr);} 71 } else { 72 *result = NULL; 73 } 74 } 75 # endif 76 #endif 77 78 if (!*result) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 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 ierr; 167 if (!currentmktype) ierr = memkind_posix_memalign(MEMKIND_DEFAULT,&newResult,PETSC_MEMALIGN,mem); 168 else ierr = memkind_posix_memalign(MEMKIND_HBW_PREFERRED,&newResult,PETSC_MEMALIGN,mem); 169 if (ierr == EINVAL) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_MEM,"Memkind: invalid 3rd or 4th argument of memkind_posix_memalign()"); 170 if (ierr == ENOMEM) PetscInfo1(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 if (petscsetmallocvisited && (imalloc != PetscTrMalloc || ifree != PetscTrFree)) SETERRQ(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 PetscErrorCode ierr; 259 PetscLogDouble mem,mal; 260 static PetscLogDouble oldmem = 0,oldmal = 0; 261 262 PetscFunctionBegin; 263 ierr = PetscMemoryGetCurrentUsage(&mem);CHKERRQ(ierr); 264 ierr = PetscMallocGetCurrentUsage(&mal);CHKERRQ(ierr); 265 266 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); 267 oldmem = mem; 268 oldmal = mal; 269 PetscFunctionReturn(0); 270 } 271 272 static PetscErrorCode (*PetscTrMallocOld)(size_t,PetscBool,int,const char[],const char[],void**) = PetscMallocAlign; 273 static PetscErrorCode (*PetscTrReallocOld)(size_t,int,const char[],const char[],void**) = PetscReallocAlign; 274 static PetscErrorCode (*PetscTrFreeOld)(void*,int,const char[],const char[]) = PetscFreeAlign; 275 276 /*@C 277 PetscMallocSetDRAM - Set PetscMalloc to use DRAM. 278 If memkind is available, change the memkind type. Otherwise, switch the 279 current malloc and free routines to the PetscMallocAlign and 280 PetscFreeAlign (PETSc default). 281 282 Not Collective 283 284 Level: developer 285 286 Notes: 287 This provides a way to do the allocation on DRAM temporarily. One 288 can switch back to the previous choice by calling PetscMallocReset(). 289 290 .seealso: PetscMallocReset() 291 @*/ 292 PetscErrorCode PetscMallocSetDRAM(void) 293 { 294 PetscFunctionBegin; 295 if (PetscTrMalloc == PetscMallocAlign) { 296 #if defined(PETSC_HAVE_MEMKIND) 297 previousmktype = currentmktype; 298 currentmktype = PETSC_MK_DEFAULT; 299 #endif 300 } else { 301 /* Save the previous choice */ 302 PetscTrMallocOld = PetscTrMalloc; 303 PetscTrReallocOld = PetscTrRealloc; 304 PetscTrFreeOld = PetscTrFree; 305 PetscTrMalloc = PetscMallocAlign; 306 PetscTrFree = PetscFreeAlign; 307 PetscTrRealloc = PetscReallocAlign; 308 } 309 PetscFunctionReturn(0); 310 } 311 312 /*@C 313 PetscMallocResetDRAM - Reset the changes made by PetscMallocSetDRAM 314 315 Not Collective 316 317 Level: developer 318 319 .seealso: PetscMallocSetDRAM() 320 @*/ 321 PetscErrorCode PetscMallocResetDRAM(void) 322 { 323 PetscFunctionBegin; 324 if (PetscTrMalloc == PetscMallocAlign) { 325 #if defined(PETSC_HAVE_MEMKIND) 326 currentmktype = previousmktype; 327 #endif 328 } else { 329 /* Reset to the previous choice */ 330 PetscTrMalloc = PetscTrMallocOld; 331 PetscTrRealloc = PetscTrReallocOld; 332 PetscTrFree = PetscTrFreeOld; 333 } 334 PetscFunctionReturn(0); 335 } 336 337 static PetscBool petscmalloccoalesce = 338 #if defined(PETSC_USE_MALLOC_COALESCED) 339 PETSC_TRUE; 340 #else 341 PETSC_FALSE; 342 #endif 343 344 /*@C 345 PetscMallocSetCoalesce - Use coalesced malloc when allocating groups of objects 346 347 Not Collective 348 349 Input Parameters: 350 . coalesce - PETSC_TRUE to use coalesced malloc for multi-object allocation. 351 352 Options Database Keys: 353 . -malloc_coalesce - turn coalesced malloc on or off 354 355 Note: 356 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. 357 This function can only be called immediately after PetscInitialize() 358 359 Level: developer 360 361 .seealso: PetscMallocA() 362 @*/ 363 PetscErrorCode PetscMallocSetCoalesce(PetscBool coalesce) 364 { 365 PetscFunctionBegin; 366 petscmalloccoalesce = coalesce; 367 PetscFunctionReturn(0); 368 } 369 370 /*@C 371 PetscMallocA - Allocate and optionally clear one or more objects, possibly using coalesced malloc 372 373 Not Collective 374 375 Input Parameters: 376 + n - number of objects to allocate (at least 1) 377 . clear - use calloc() to allocate space initialized to zero 378 . lineno - line number to attribute allocation (typically __LINE__) 379 . function - function to attribute allocation (typically PETSC_FUNCTION_NAME) 380 . filename - file name to attribute allocation (typically __FILE__) 381 - bytes0 - first of n object sizes 382 383 Output Parameters: 384 . ptr0 - first of n pointers to allocate 385 386 Notes: 387 This function is not normally called directly by users, but rather via the macros PetscMalloc1(), PetscMalloc2(), or PetscCalloc1(), etc. 388 389 Level: developer 390 391 .seealso: PetscMallocAlign(), PetscMallocSet(), PetscMalloc1(), PetscMalloc2(), PetscMalloc3(), PetscMalloc4(), PetscMalloc5(), PetscMalloc6(), PetscMalloc7(), PetscCalloc1(), PetscCalloc2(), PetscCalloc3(), PetscCalloc4(), PetscCalloc5(), PetscCalloc6(), PetscCalloc7(), PetscFreeA() 392 @*/ 393 PetscErrorCode PetscMallocA(int n,PetscBool clear,int lineno,const char *function,const char *filename,size_t bytes0,void *ptr0,...) 394 { 395 PetscErrorCode ierr; 396 va_list Argp; 397 size_t bytes[8],sumbytes; 398 void **ptr[8]; 399 int i; 400 401 PetscFunctionBegin; 402 if (n > 8) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Attempt to allocate %d objects but only 8 supported",n); 403 bytes[0] = bytes0; 404 ptr[0] = (void**)ptr0; 405 sumbytes = (bytes0 + PETSC_MEMALIGN-1) & ~(PETSC_MEMALIGN-1); 406 va_start(Argp,ptr0); 407 for (i=1; i<n; i++) { 408 bytes[i] = va_arg(Argp,size_t); 409 ptr[i] = va_arg(Argp,void**); 410 sumbytes += (bytes[i] + PETSC_MEMALIGN-1) & ~(PETSC_MEMALIGN-1); 411 } 412 va_end(Argp); 413 if (petscmalloccoalesce) { 414 char *p; 415 ierr = (*PetscTrMalloc)(sumbytes,clear,lineno,function,filename,(void**)&p);CHKERRQ(ierr); 416 for (i=0; i<n; i++) { 417 *ptr[i] = bytes[i] ? p : NULL; 418 p = (char*)PetscAddrAlign(p + bytes[i]); 419 } 420 } else { 421 for (i=0; i<n; i++) { 422 ierr = (*PetscTrMalloc)(bytes[i],clear,lineno,function,filename,(void**)ptr[i]);CHKERRQ(ierr); 423 } 424 } 425 PetscFunctionReturn(0); 426 } 427 428 /*@C 429 PetscFreeA - Free one or more objects, possibly allocated using coalesced malloc 430 431 Not Collective 432 433 Input Parameters: 434 + n - number of objects to free (at least 1) 435 . lineno - line number to attribute deallocation (typically __LINE__) 436 . function - function to attribute deallocation (typically PETSC_FUNCTION_NAME) 437 . filename - file name to attribute deallocation (typically __FILE__) 438 - ptr0 ... - first of n pointers to free 439 440 Note: 441 This function is not normally called directly by users, but rather via the macros PetscFree(), PetscFree2(), etc. 442 443 The pointers are zeroed to prevent users from accidently reusing space that has been freed. 444 445 Level: developer 446 447 .seealso: PetscMallocAlign(), PetscMallocSet(), PetscMallocA(), PetscFree1(), PetscFree2(), PetscFree3(), PetscFree4(), PetscFree5(), PetscFree6(), PetscFree7() 448 @*/ 449 PetscErrorCode PetscFreeA(int n,int lineno,const char *function,const char *filename,void *ptr0,...) 450 { 451 PetscErrorCode ierr; 452 va_list Argp; 453 void **ptr[8]; 454 int i; 455 456 PetscFunctionBegin; 457 if (n > 8) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Attempt to allocate %d objects but only up to 8 supported",n); 458 ptr[0] = (void**)ptr0; 459 va_start(Argp,ptr0); 460 for (i=1; i<n; i++) { 461 ptr[i] = va_arg(Argp,void**); 462 } 463 va_end(Argp); 464 if (petscmalloccoalesce) { 465 for (i=0; i<n; i++) { /* Find first nonempty allocation */ 466 if (*ptr[i]) break; 467 } 468 while (--n > i) { 469 *ptr[n] = NULL; 470 } 471 ierr = (*PetscTrFree)(*ptr[n],lineno,function,filename);CHKERRQ(ierr); 472 *ptr[n] = NULL; 473 } else { 474 while (--n >= 0) { 475 ierr = (*PetscTrFree)(*ptr[n],lineno,function,filename);CHKERRQ(ierr); 476 *ptr[n] = NULL; 477 } 478 } 479 PetscFunctionReturn(0); 480 } 481