1 2 /* 3 Code that allows a user to dictate what malloc() PETSc uses. 4 */ 5 #include <petscsys.h> /*I "petscsys.h" I*/ 6 #include <stdarg.h> 7 #if defined(PETSC_HAVE_MALLOC_H) 8 #include <malloc.h> 9 #endif 10 #if defined(PETSC_HAVE_MEMKIND) 11 #include <errno.h> 12 #include <memkind.h> 13 typedef enum {PETSC_MK_DEFAULT=0,PETSC_MK_HBW_PREFERRED=1} PetscMemkindType; 14 PetscMemkindType currentmktype = PETSC_MK_HBW_PREFERRED; 15 PetscMemkindType previousmktype = PETSC_MK_HBW_PREFERRED; 16 #endif 17 /* 18 We want to make sure that all mallocs of double or complex numbers are complex aligned. 19 1) on systems with memalign() we call that routine to get an aligned memory location 20 2) on systems without memalign() we 21 - allocate one sizeof(PetscScalar) extra space 22 - we shift the pointer up slightly if needed to get PetscScalar aligned 23 - if shifted we store at ptr[-1] the amount of shift (plus a classid) 24 */ 25 #define SHIFT_CLASSID 456123 26 27 PetscErrorCode PetscMallocAlign(size_t mem,int line,const char func[],const char file[],void **result) 28 { 29 if (!mem) { *result = NULL; return 0; } 30 #if defined(PETSC_HAVE_MEMKIND) 31 { 32 int ierr; 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 } 38 #else 39 # if defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8) 40 *result = malloc(mem); 41 # elif defined(PETSC_HAVE_MEMALIGN) 42 *result = memalign(PETSC_MEMALIGN,mem); 43 # else 44 { 45 /* 46 malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned 47 */ 48 int *ptr = (int*)malloc(mem + 2*PETSC_MEMALIGN); 49 if (ptr) { 50 int shift = (int)(((PETSC_UINTPTR_T) ptr) % PETSC_MEMALIGN); 51 shift = (2*PETSC_MEMALIGN - shift)/sizeof(int); 52 ptr[shift-1] = shift + SHIFT_CLASSID; 53 ptr += shift; 54 *result = (void*)ptr; 55 } else { 56 *result = NULL; 57 } 58 } 59 # endif 60 #endif 61 if (!*result) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 62 return 0; 63 } 64 65 PetscErrorCode PetscFreeAlign(void *ptr,int line,const char func[],const char file[]) 66 { 67 if (!ptr) return 0; 68 #if defined(PETSC_HAVE_MEMKIND) 69 memkind_free(0,ptr); /* specify the kind to 0 so that memkind will look up for the right type */ 70 #else 71 # if (!(defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !defined(PETSC_HAVE_MEMALIGN)) 72 { 73 /* 74 Previous int tells us how many ints the pointer has been shifted from 75 the original address provided by the system malloc(). 76 */ 77 int shift = *(((int*)ptr)-1) - SHIFT_CLASSID; 78 if (shift > PETSC_MEMALIGN-1) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap"); 79 if (shift < 0) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap"); 80 ptr = (void*)(((int*)ptr) - shift); 81 } 82 # endif 83 84 # if defined(PETSC_HAVE_FREE_RETURN_INT) 85 int err = free(ptr); 86 if (err) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"System free returned error %d\n",err); 87 # else 88 free(ptr); 89 # endif 90 #endif 91 return 0; 92 } 93 94 PetscErrorCode PetscReallocAlign(size_t mem, int line, const char func[], const char file[], void **result) 95 { 96 PetscErrorCode ierr; 97 98 if (!mem) { 99 ierr = PetscFreeAlign(*result, line, func, file); 100 if (ierr) return ierr; 101 *result = NULL; 102 return 0; 103 } 104 #if defined(PETSC_HAVE_MEMKIND) 105 if (!currentmktype) *result = memkind_realloc(MEMKIND_DEFAULT,*result,mem); 106 else *result = memkind_realloc(MEMKIND_HBW_PREFERRED,*result,mem); 107 #else 108 # if (!(defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !defined(PETSC_HAVE_MEMALIGN)) 109 { 110 /* 111 Previous int tells us how many ints the pointer has been shifted from 112 the original address provided by the system malloc(). 113 */ 114 int shift = *(((int*)*result)-1) - SHIFT_CLASSID; 115 if (shift > PETSC_MEMALIGN-1) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap"); 116 if (shift < 0) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap"); 117 *result = (void*)(((int*)*result) - shift); 118 } 119 # endif 120 121 # if (defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) || defined(PETSC_HAVE_MEMALIGN) 122 *result = realloc(*result, mem); 123 # else 124 { 125 /* 126 malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned 127 */ 128 int *ptr = (int *) realloc(*result, mem + 2*PETSC_MEMALIGN); 129 if (ptr) { 130 int shift = (int)(((PETSC_UINTPTR_T) ptr) % PETSC_MEMALIGN); 131 shift = (2*PETSC_MEMALIGN - shift)/sizeof(int); 132 ptr[shift-1] = shift + SHIFT_CLASSID; 133 ptr += shift; 134 *result = (void*)ptr; 135 } else { 136 *result = NULL; 137 } 138 } 139 # endif 140 #endif 141 if (!*result) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 142 #if defined(PETSC_HAVE_MEMALIGN) 143 /* There are no standard guarantees that realloc() maintains the alignment of memalign(), so I think we have to 144 * realloc and, if the alignment is wrong, malloc/copy/free. */ 145 if (((size_t) (*result)) % PETSC_MEMALIGN) { 146 void *newResult; 147 # if defined(PETSC_HAVE_MEMKIND) 148 { 149 int ierr; 150 if (!currentmktype) ierr = memkind_posix_memalign(MEMKIND_DEFAULT,&newResult,PETSC_MEMALIGN,mem); 151 else ierr = memkind_posix_memalign(MEMKIND_HBW_PREFERRED,&newResult,PETSC_MEMALIGN,mem); 152 if (ierr == EINVAL) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_MEM,"Memkind: invalid 3rd or 4th argument of memkind_posix_memalign()"); 153 if (ierr == ENOMEM) PetscInfo1(0,"Memkind: fail to request HBW memory %.0f, falling back to normal memory\n",(PetscLogDouble)mem); 154 } 155 # else 156 newResult = memalign(PETSC_MEMALIGN,mem); 157 # endif 158 if (!newResult) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem); 159 ierr = PetscMemcpy(newResult,*result,mem); 160 if (ierr) return ierr; 161 # if defined(PETSC_HAVE_FREE_RETURN_INT) 162 { 163 int err = free(*result); 164 if (err) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"System free returned error %d\n",err); 165 } 166 # else 167 # if defined(PETSC_HAVE_MEMKIND) 168 memkind_free(0,*result); 169 # else 170 free(*result); 171 # endif 172 # endif 173 *result = newResult; 174 } 175 #endif 176 return 0; 177 } 178 179 PetscErrorCode (*PetscTrMalloc)(size_t,int,const char[],const char[],void**) = PetscMallocAlign; 180 PetscErrorCode (*PetscTrFree)(void*,int,const char[],const char[]) = PetscFreeAlign; 181 PetscErrorCode (*PetscTrRealloc)(size_t,int,const char[],const char[],void**) = PetscReallocAlign; 182 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 . lineno - line number to attribute allocation (typically __LINE__) 353 . function - function to attribute allocation (typically PETSC_FUNCTION_NAME) 354 . filename - file name to attribute allocation (typically __FILE__) 355 - bytes0 - first of n object sizes 356 357 Output Parameters: 358 . ptr0 - first of n pointers to allocate 359 360 Notes: 361 This function is not normally called directly by users, but rather via the macros PetscMalloc1(), PetscMalloc2(), or PetscCalloc1(), etc. 362 363 Level: developer 364 365 .seealso: PetscMallocAlign(), PetscMallocSet(), PetscMalloc1(), PetscMalloc2(), PetscMalloc3(), PetscMalloc4(), PetscMalloc5(), PetscMalloc6(), PetscMalloc7(), PetscCalloc1(), PetscCalloc2(), PetscCalloc3(), PetscCalloc4(), PetscCalloc5(), PetscCalloc6(), PetscCalloc7(), PetscFreeA() 366 @*/ 367 PetscErrorCode PetscMallocA(int n,PetscBool clear,int lineno,const char *function,const char *filename,size_t bytes0,void *ptr0,...) 368 { 369 PetscErrorCode ierr; 370 va_list Argp; 371 size_t bytes[8],sumbytes; 372 void **ptr[8]; 373 int i; 374 375 PetscFunctionBegin; 376 if (n > 8) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Attempt to allocate %d objects but only 8 supported",n); 377 bytes[0] = bytes0; 378 ptr[0] = (void**)ptr0; 379 sumbytes = (bytes0 + PETSC_MEMALIGN-1) & ~(PETSC_MEMALIGN-1); 380 va_start(Argp,ptr0); 381 for (i=1; i<n; i++) { 382 bytes[i] = va_arg(Argp,size_t); 383 ptr[i] = va_arg(Argp,void**); 384 sumbytes += (bytes[i] + PETSC_MEMALIGN-1) & ~(PETSC_MEMALIGN-1); 385 } 386 va_end(Argp); 387 if (petscmalloccoalesce) { 388 char *p; 389 ierr = (*PetscTrMalloc)(sumbytes,lineno,function,filename,(void**)&p);CHKERRQ(ierr); 390 for (i=0; i<n; i++) { 391 *ptr[i] = bytes[i] ? p : NULL; 392 p = (char*)PetscAddrAlign(p + bytes[i]); 393 } 394 } else { 395 for (i=0; i<n; i++) { 396 ierr = (*PetscTrMalloc)(bytes[i],lineno,function,filename,(void**)ptr[i]);CHKERRQ(ierr); 397 } 398 } 399 if (clear) { 400 for (i=0; i<n; i++) { 401 ierr = PetscMemzero(*ptr[i],bytes[i]);CHKERRQ(ierr); 402 } 403 } 404 PetscFunctionReturn(0); 405 } 406 407 /*@C 408 PetscFreeA - Free one or more objects, possibly allocated using coalesced malloc 409 410 Not Collective 411 412 Input Parameters: 413 + n - number of objects to free (at least 1) 414 . lineno - line number to attribute deallocation (typically __LINE__) 415 . function - function to attribute deallocation (typically PETSC_FUNCTION_NAME) 416 . filename - file name to attribute deallocation (typically __FILE__) 417 - ptr0 ... - first of n pointers to free 418 419 Note: 420 This function is not normally called directly by users, but rather via the macros PetscFree1(), PetscFree2(), etc. 421 422 Level: developer 423 424 .seealso: PetscMallocAlign(), PetscMallocSet(), PetscMallocA(), PetscFree1(), PetscFree2(), PetscFree3(), PetscFree4(), PetscFree5(), PetscFree6(), PetscFree7() 425 @*/ 426 PetscErrorCode PetscFreeA(int n,int lineno,const char *function,const char *filename,void *ptr0,...) 427 { 428 PetscErrorCode ierr; 429 va_list Argp; 430 void **ptr[8]; 431 int i; 432 433 PetscFunctionBegin; 434 if (n > 8) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Attempt to allocate %d objects but only 8 supported",n); 435 ptr[0] = (void**)ptr0; 436 va_start(Argp,ptr0); 437 for (i=1; i<n; i++) { 438 ptr[i] = va_arg(Argp,void**); 439 } 440 va_end(Argp); 441 if (petscmalloccoalesce) { 442 for (i=0; i<n; i++) { /* Find first nonempty allocation */ 443 if (*ptr[i]) break; 444 } 445 while (--n > i) { 446 *ptr[n] = NULL; 447 } 448 ierr = (*PetscTrFree)(*ptr[n],lineno,function,filename);CHKERRQ(ierr); 449 *ptr[n] = NULL; 450 } else { 451 while (--n >= 0) { 452 ierr = (*PetscTrFree)(*ptr[n],lineno,function,filename);CHKERRQ(ierr); 453 *ptr[n] = NULL; 454 } 455 } 456 PetscFunctionReturn(0); 457 } 458