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