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