xref: /petsc/src/sys/memory/mal.c (revision fc2a71443518d2bbcbbf4df1ca0161c9e97223a4)
1 
2 /*
3     Code that allows a user to dictate what malloc() PETSc uses.
4 */
5 #include <petscsys.h>             /*I   "petscsys.h"   I*/
6 #if defined(PETSC_HAVE_MALLOC_H)
7 #include <malloc.h>
8 #endif
9 #if defined(PETSC_HAVE_MEMKIND)
10 #include <memkind.h>
11 typedef enum {PETSC_MK_DEFAULT=0,PETSC_MK_HBW_PREFERRED=1} PetscMemkindType;
12 PetscMemkindType currentmktype = PETSC_MK_HBW_PREFERRED;
13 PetscMemkindType previousmktype = PETSC_MK_HBW_PREFERRED;
14 #endif
15 /*
16         We want to make sure that all mallocs of double or complex numbers are complex aligned.
17     1) on systems with memalign() we call that routine to get an aligned memory location
18     2) on systems without memalign() we
19        - allocate one sizeof(PetscScalar) extra space
20        - we shift the pointer up slightly if needed to get PetscScalar aligned
21        - if shifted we store at ptr[-1] the amount of shift (plus a classid)
22 */
23 #define SHIFT_CLASSID 456123
24 
25 PetscErrorCode  PetscMallocAlign(size_t mem,int line,const char func[],const char file[],void **result)
26 {
27   if (!mem) { *result = NULL; return 0; }
28 #if defined(PETSC_HAVE_MEMKIND)
29   {
30     int ierr;
31     if (!currentmktype) ierr = memkind_posix_memalign(MEMKIND_DEFAULT,result,PETSC_MEMALIGN,mem);
32     else ierr = memkind_posix_memalign(MEMKIND_HBW_PREFERRED,result,PETSC_MEMALIGN,mem);
33     if (ierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_MEM,"Memory requested with memkind %.0f",(PetscLogDouble)mem);
34   }
35 #else
36 #  if defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)
37   *result = malloc(mem);
38 #  elif defined(PETSC_HAVE_MEMALIGN)
39   *result = memalign(PETSC_MEMALIGN,mem);
40 #  else
41   {
42     /*
43       malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned
44     */
45     if (ptr) {
46       int shift    = (int)(((PETSC_UINTPTR_T) ptr) % PETSC_MEMALIGN);
47       shift        = (2*PETSC_MEMALIGN - shift)/sizeof(int);
48       ptr[shift-1] = shift + SHIFT_CLASSID;
49       ptr         += shift;
50       *result      = (void*)ptr;
51     } else {
52       *result      = NULL;
53     }
54   }
55 #  endif
56 #endif
57   if (!*result) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem);
58   return 0;
59 }
60 
61 PetscErrorCode  PetscFreeAlign(void *ptr,int line,const char func[],const char file[])
62 {
63   if (!ptr) return 0;
64 #if defined(PETSC_HAVE_MEMKIND)
65   memkind_free(0,ptr); /* specify the kind to 0 so that memkind will look up for the right type */
66 #else
67 #  if (!(defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !defined(PETSC_HAVE_MEMALIGN))
68   {
69     /*
70       Previous int tells us how many ints the pointer has been shifted from
71       the original address provided by the system malloc().
72     */
73     int shift = *(((int*)ptr)-1) - SHIFT_CLASSID;
74     if (shift > PETSC_MEMALIGN-1) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap");
75     if (shift < 0) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap");
76     ptr = (void*)(((int*)ptr) - shift);
77   }
78 #  endif
79 
80 #  if defined(PETSC_HAVE_FREE_RETURN_INT)
81   int err = free(ptr);
82   if (err) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"System free returned error %d\n",err);
83 #  else
84   free(ptr);
85 #  endif
86 #endif
87   return 0;
88 }
89 
90 PetscErrorCode PetscReallocAlign(size_t mem, int line, const char func[], const char file[], void **result)
91 {
92   PetscErrorCode ierr;
93 
94   if (!mem) {
95     ierr = PetscFreeAlign(*result, line, func, file);
96     if (ierr) return ierr;
97     *result = NULL;
98     return 0;
99   }
100 #if defined(PETSC_HAVE_MEMKIND)
101   if (!currentmktype) *result = memkind_realloc(MEMKIND_DEFAULT,*result,mem);
102   else *result = memkind_realloc(MEMKIND_HBW_PREFERRED,*result,mem);
103 #else
104 #  if (!(defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !defined(PETSC_HAVE_MEMALIGN))
105   {
106     /*
107       Previous int tells us how many ints the pointer has been shifted from
108       the original address provided by the system malloc().
109     */
110     int shift = *(((int*)*result)-1) - SHIFT_CLASSID;
111     if (shift > PETSC_MEMALIGN-1) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap");
112     if (shift < 0) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap");
113     *result = (void*)(((int*)*result) - shift);
114   }
115 #  endif
116 
117 #  if (defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) || defined(PETSC_HAVE_MEMALIGN)
118   *result = realloc(*result, mem);
119 #  else
120   {
121     /*
122       malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned
123     */
124     int *ptr = (int *) realloc(*result, mem + 2*PETSC_MEMALIGN);
125     if (ptr) {
126       int shift    = (int)(((PETSC_UINTPTR_T) ptr) % PETSC_MEMALIGN);
127       shift        = (2*PETSC_MEMALIGN - shift)/sizeof(int);
128       ptr[shift-1] = shift + SHIFT_CLASSID;
129       ptr         += shift;
130       *result      = (void*)ptr;
131     } else {
132       *result      = NULL;
133     }
134   }
135 #  endif
136 #endif
137   if (!*result) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem);
138 #if defined(PETSC_HAVE_MEMALIGN)
139   /* There are no standard guarantees that realloc() maintains the alignment of memalign(), so I think we have to
140    * realloc and, if the alignment is wrong, malloc/copy/free. */
141   if (((size_t) (*result)) % PETSC_MEMALIGN) {
142     void *newResult;
143 #  if defined(PETSC_HAVE_MEMKIND)
144     {
145       int ierr;
146       if (!currentmktype) ierr = memkind_posix_memalign(MEMKIND_DEFAULT,&newResult,PETSC_MEMALIGN,mem);
147       else ierr = memkind_posix_memalign(MEMKIND_HBW_PREFERRED,&newResult,PETSC_MEMALIGN,mem);
148       if (ierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_MEM,"Memory requested with memkind %.0f",(PetscLogDouble)mem);
149     }
150 #  else
151     newResult = memalign(PETSC_MEMALIGN,mem);
152 #  endif
153     if (!newResult) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem);
154     ierr = PetscMemcpy(newResult,*result,mem);
155     if (ierr) return ierr;
156 #  if defined(PETSC_HAVE_FREE_RETURN_INT)
157     {
158       int err = free(*result);
159       if (err) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"System free returned error %d\n",err);
160     }
161 #  else
162 #    if defined(PETSC_HAVE_MEMKIND)
163     memkind_free(0,*result);
164 #    else
165     free(*result);
166 #    endif
167 #  endif
168     *result = newResult;
169   }
170 #endif
171   return 0;
172 }
173 
174 PetscErrorCode (*PetscTrMalloc)(size_t,int,const char[],const char[],void**) = PetscMallocAlign;
175 PetscErrorCode (*PetscTrFree)(void*,int,const char[],const char[])           = PetscFreeAlign;
176 PetscErrorCode (*PetscTrRealloc)(size_t,int,const char[],const char[],void**) = PetscReallocAlign;
177 
178 PetscBool petscsetmallocvisited = PETSC_FALSE;
179 
180 /*@C
181    PetscMallocSet - Sets the routines used to do mallocs and frees.
182    This routine MUST be called before PetscInitialize() and may be
183    called only once.
184 
185    Not Collective
186 
187    Input Parameters:
188 +  malloc - the malloc routine
189 -  free - the free routine
190 
191    Level: developer
192 
193    Concepts: malloc
194    Concepts: memory^allocation
195 
196 @*/
197 PetscErrorCode  PetscMallocSet(PetscErrorCode (*imalloc)(size_t,int,const char[],const char[],void**),
198                                               PetscErrorCode (*ifree)(void*,int,const char[],const char[]))
199 {
200   PetscFunctionBegin;
201   if (petscsetmallocvisited && (imalloc != PetscTrMalloc || ifree != PetscTrFree)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"cannot call multiple times");
202   PetscTrMalloc         = imalloc;
203   PetscTrFree           = ifree;
204   petscsetmallocvisited = PETSC_TRUE;
205   PetscFunctionReturn(0);
206 }
207 
208 /*@C
209    PetscMallocClear - Resets the routines used to do mallocs and frees to the
210         defaults.
211 
212    Not Collective
213 
214    Level: developer
215 
216    Notes:
217     In general one should never run a PETSc program with different malloc() and
218     free() settings for different parts; this is because one NEVER wants to
219     free() an address that was malloced by a different memory management system
220 
221 @*/
222 PetscErrorCode  PetscMallocClear(void)
223 {
224   PetscFunctionBegin;
225   PetscTrMalloc         = PetscMallocAlign;
226   PetscTrFree           = PetscFreeAlign;
227   petscsetmallocvisited = PETSC_FALSE;
228   PetscFunctionReturn(0);
229 }
230 
231 PetscErrorCode PetscMemoryTrace(const char label[])
232 {
233   PetscErrorCode        ierr;
234   PetscLogDouble        mem,mal;
235   static PetscLogDouble oldmem = 0,oldmal = 0;
236 
237   PetscFunctionBegin;
238   ierr = PetscMemoryGetCurrentUsage(&mem);CHKERRQ(ierr);
239   ierr = PetscMallocGetCurrentUsage(&mal);CHKERRQ(ierr);
240 
241   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);
242   oldmem = mem;
243   oldmal = mal;
244   PetscFunctionReturn(0);
245 }
246 
247 static PetscErrorCode (*PetscTrMallocOld)(size_t,int,const char[],const char[],void**) = PetscMallocAlign;
248 static PetscErrorCode (*PetscTrFreeOld)(void*,int,const char[],const char[])           = PetscFreeAlign;
249 
250 /*@C
251    PetscMallocSetDRAM - Set PetscMalloc to use DRAM.
252      If memkind is available, change the memkind type. Otherwise, switch the
253      current malloc and free routines to the PetscMallocAlign and
254      PetscFreeAlign (PETSc default).
255 
256    Not Collective
257 
258    Level: developer
259 
260    Notes:
261      This provides a way to do the allocation on DRAM temporarily. One
262      can switch back to the previous choice by calling PetscMallocReset().
263 
264 .seealso: PetscMallocReset()
265 @*/
266 PetscErrorCode PetscMallocSetDRAM(void)
267 {
268   PetscFunctionBegin;
269   if (PetscTrMalloc == PetscMallocAlign) {
270 #if defined(PETSC_HAVE_MEMKIND)
271     previousmktype = currentmktype;
272     currentmktype  = PETSC_MK_DEFAULT;
273 #endif
274   } else {
275     /* Save the previous choice */
276     PetscTrMallocOld = PetscTrMalloc;
277     PetscTrFreeOld   = PetscTrFree;
278     PetscTrMalloc    = PetscMallocAlign;
279     PetscTrFree      = PetscFreeAlign;
280   }
281   PetscFunctionReturn(0);
282 }
283 
284 /*@C
285    PetscMallocResetDRAM - Reset the changes made by PetscMallocSetDRAM
286 
287    Not Collective
288 
289    Level: developer
290 
291 .seealso: PetscMallocSetDRAM()
292 @*/
293 PetscErrorCode PetscMallocResetDRAM(void)
294 {
295   PetscFunctionBegin;
296   if (PetscTrMalloc == PetscMallocAlign) {
297 #if defined(PETSC_HAVE_MEMKIND)
298     currentmktype = previousmktype;
299 #endif
300   } else {
301     /* Reset to the previous choice */
302     PetscTrMalloc = PetscTrMallocOld;
303     PetscTrFree   = PetscTrFreeOld;
304   }
305   PetscFunctionReturn(0);
306 }
307