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