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