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