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 199 @*/ 200 PetscErrorCode PetscMallocSet(PetscErrorCode (*imalloc)(size_t,int,const char[],const char[],void**), 201 PetscErrorCode (*ifree)(void*,int,const char[],const char[])) 202 { 203 PetscFunctionBegin; 204 if (petscsetmallocvisited && (imalloc != PetscTrMalloc || ifree != PetscTrFree)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"cannot call multiple times"); 205 PetscTrMalloc = imalloc; 206 PetscTrFree = ifree; 207 petscsetmallocvisited = PETSC_TRUE; 208 PetscFunctionReturn(0); 209 } 210 211 /*@C 212 PetscMallocClear - Resets the routines used to do mallocs and frees to the 213 defaults. 214 215 Not Collective 216 217 Level: developer 218 219 Notes: 220 In general one should never run a PETSc program with different malloc() and 221 free() settings for different parts; this is because one NEVER wants to 222 free() an address that was malloced by a different memory management system 223 224 @*/ 225 PetscErrorCode PetscMallocClear(void) 226 { 227 PetscFunctionBegin; 228 PetscTrMalloc = PetscMallocAlign; 229 PetscTrFree = PetscFreeAlign; 230 petscsetmallocvisited = PETSC_FALSE; 231 PetscFunctionReturn(0); 232 } 233 234 PetscErrorCode PetscMemoryTrace(const char label[]) 235 { 236 PetscErrorCode ierr; 237 PetscLogDouble mem,mal; 238 static PetscLogDouble oldmem = 0,oldmal = 0; 239 240 PetscFunctionBegin; 241 ierr = PetscMemoryGetCurrentUsage(&mem);CHKERRQ(ierr); 242 ierr = PetscMallocGetCurrentUsage(&mal);CHKERRQ(ierr); 243 244 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); 245 oldmem = mem; 246 oldmal = mal; 247 PetscFunctionReturn(0); 248 } 249 250 static PetscErrorCode (*PetscTrMallocOld)(size_t,int,const char[],const char[],void**) = PetscMallocAlign; 251 static PetscErrorCode (*PetscTrFreeOld)(void*,int,const char[],const char[]) = PetscFreeAlign; 252 253 /*@C 254 PetscMallocSetDRAM - Set PetscMalloc to use DRAM. 255 If memkind is available, change the memkind type. Otherwise, switch the 256 current malloc and free routines to the PetscMallocAlign and 257 PetscFreeAlign (PETSc default). 258 259 Not Collective 260 261 Level: developer 262 263 Notes: 264 This provides a way to do the allocation on DRAM temporarily. One 265 can switch back to the previous choice by calling PetscMallocReset(). 266 267 .seealso: PetscMallocReset() 268 @*/ 269 PetscErrorCode PetscMallocSetDRAM(void) 270 { 271 PetscFunctionBegin; 272 if (PetscTrMalloc == PetscMallocAlign) { 273 #if defined(PETSC_HAVE_MEMKIND) 274 previousmktype = currentmktype; 275 currentmktype = PETSC_MK_DEFAULT; 276 #endif 277 } else { 278 /* Save the previous choice */ 279 PetscTrMallocOld = PetscTrMalloc; 280 PetscTrFreeOld = PetscTrFree; 281 PetscTrMalloc = PetscMallocAlign; 282 PetscTrFree = PetscFreeAlign; 283 } 284 PetscFunctionReturn(0); 285 } 286 287 /*@C 288 PetscMallocResetDRAM - Reset the changes made by PetscMallocSetDRAM 289 290 Not Collective 291 292 Level: developer 293 294 .seealso: PetscMallocSetDRAM() 295 @*/ 296 PetscErrorCode PetscMallocResetDRAM(void) 297 { 298 PetscFunctionBegin; 299 if (PetscTrMalloc == PetscMallocAlign) { 300 #if defined(PETSC_HAVE_MEMKIND) 301 currentmktype = previousmktype; 302 #endif 303 } else { 304 /* Reset to the previous choice */ 305 PetscTrMalloc = PetscTrMallocOld; 306 PetscTrFree = PetscTrFreeOld; 307 } 308 PetscFunctionReturn(0); 309 } 310 311 static PetscBool petscmalloccoalesce = 312 #if defined(PETSC_USE_MALLOC_COALESCED) 313 PETSC_TRUE; 314 #else 315 PETSC_FALSE; 316 #endif 317 318 /*@C 319 PetscMallocSetCoalesce - Use coalesced malloc when allocating groups of objects 320 321 Not Collective 322 323 Input Parameters: 324 . coalesce - PETSC_TRUE to use coalesced malloc for multi-object allocation. 325 326 Options Database Keys: 327 . -malloc_coalesce - turn coalesced malloc on or off 328 329 Note: 330 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. 331 332 Level: developer 333 334 .seealso: PetscMallocA() 335 @*/ 336 PetscErrorCode PetscMallocSetCoalesce(PetscBool coalesce) 337 { 338 PetscFunctionBegin; 339 petscmalloccoalesce = coalesce; 340 PetscFunctionReturn(0); 341 } 342 343 /*@C 344 PetscMallocA - Allocate and optionally clear one or more objects, possibly using coalesced malloc 345 346 Not Collective 347 348 Input Parameters: 349 + n - number of objects to allocate (at least 1) 350 . clear - use calloc() to allocate space initialized to zero 351 . lineno - line number to attribute allocation (typically __LINE__) 352 . function - function to attribute allocation (typically PETSC_FUNCTION_NAME) 353 . filename - file name to attribute allocation (typically __FILE__) 354 - bytes0 - first of n object sizes 355 356 Output Parameters: 357 . ptr0 - first of n pointers to allocate 358 359 Notes: 360 This function is not normally called directly by users, but rather via the macros PetscMalloc1(), PetscMalloc2(), or PetscCalloc1(), etc. 361 362 Level: developer 363 364 .seealso: PetscMallocAlign(), PetscMallocSet(), PetscMalloc1(), PetscMalloc2(), PetscMalloc3(), PetscMalloc4(), PetscMalloc5(), PetscMalloc6(), PetscMalloc7(), PetscCalloc1(), PetscCalloc2(), PetscCalloc3(), PetscCalloc4(), PetscCalloc5(), PetscCalloc6(), PetscCalloc7(), PetscFreeA() 365 @*/ 366 PetscErrorCode PetscMallocA(int n,PetscBool clear,int lineno,const char *function,const char *filename,size_t bytes0,void *ptr0,...) 367 { 368 PetscErrorCode ierr; 369 va_list Argp; 370 size_t bytes[8],sumbytes; 371 void **ptr[8]; 372 int i; 373 374 PetscFunctionBegin; 375 if (n > 8) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Attempt to allocate %d objects but only 8 supported",n); 376 bytes[0] = bytes0; 377 ptr[0] = (void**)ptr0; 378 sumbytes = (bytes0 + PETSC_MEMALIGN-1) & ~(PETSC_MEMALIGN-1); 379 va_start(Argp,ptr0); 380 for (i=1; i<n; i++) { 381 bytes[i] = va_arg(Argp,size_t); 382 ptr[i] = va_arg(Argp,void**); 383 sumbytes += (bytes[i] + PETSC_MEMALIGN-1) & ~(PETSC_MEMALIGN-1); 384 } 385 va_end(Argp); 386 if (petscmalloccoalesce) { 387 char *p; 388 ierr = (*PetscTrMalloc)(sumbytes,lineno,function,filename,(void**)&p);CHKERRQ(ierr); 389 for (i=0; i<n; i++) { 390 *ptr[i] = bytes[i] ? p : NULL; 391 p = (char*)PetscAddrAlign(p + bytes[i]); 392 } 393 } else { 394 for (i=0; i<n; i++) { 395 ierr = (*PetscTrMalloc)(bytes[i],lineno,function,filename,(void**)ptr[i]);CHKERRQ(ierr); 396 } 397 } 398 if (clear) { 399 for (i=0; i<n; i++) { 400 ierr = PetscMemzero(*ptr[i],bytes[i]);CHKERRQ(ierr); 401 } 402 } 403 PetscFunctionReturn(0); 404 } 405 406 /*@C 407 PetscFreeA - Free one or more objects, possibly allocated using coalesced malloc 408 409 Not Collective 410 411 Input Parameters: 412 + n - number of objects to free (at least 1) 413 . lineno - line number to attribute deallocation (typically __LINE__) 414 . function - function to attribute deallocation (typically PETSC_FUNCTION_NAME) 415 . filename - file name to attribute deallocation (typically __FILE__) 416 - ptr0 ... - first of n pointers to free 417 418 Note: 419 This function is not normally called directly by users, but rather via the macros PetscFree1(), PetscFree2(), etc. 420 421 The pointers are zeroed to prevent users from accidently reusing space that has been freed. 422 423 Level: developer 424 425 .seealso: PetscMallocAlign(), PetscMallocSet(), PetscMallocA(), PetscFree1(), PetscFree2(), PetscFree3(), PetscFree4(), PetscFree5(), PetscFree6(), PetscFree7() 426 @*/ 427 PetscErrorCode PetscFreeA(int n,int lineno,const char *function,const char *filename,void *ptr0,...) 428 { 429 PetscErrorCode ierr; 430 va_list Argp; 431 void **ptr[8]; 432 int i; 433 434 PetscFunctionBegin; 435 if (n > 8) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Attempt to allocate %d objects but only up to 8 supported",n); 436 ptr[0] = (void**)ptr0; 437 va_start(Argp,ptr0); 438 for (i=1; i<n; i++) { 439 ptr[i] = va_arg(Argp,void**); 440 } 441 va_end(Argp); 442 if (petscmalloccoalesce) { 443 for (i=0; i<n; i++) { /* Find first nonempty allocation */ 444 if (*ptr[i]) break; 445 } 446 while (--n > i) { 447 *ptr[n] = NULL; 448 } 449 ierr = (*PetscTrFree)(*ptr[n],lineno,function,filename);CHKERRQ(ierr); 450 *ptr[n] = NULL; 451 } else { 452 while (--n >= 0) { 453 ierr = (*PetscTrFree)(*ptr[n],lineno,function,filename);CHKERRQ(ierr); 454 *ptr[n] = NULL; 455 } 456 } 457 PetscFunctionReturn(0); 458 } 459