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