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