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