xref: /petsc/src/sys/memory/mal.c (revision 5f64dca9724aa3d1c0232cee09f31525f14a35bc)
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