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