xref: /petsc/src/sys/memory/mal.c (revision a69119a591a03a9d906b29c0a4e9802e4d7c9795)
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 @*/
209 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 **)) {
210   PetscFunctionBegin;
211   PetscCheck(!petscsetmallocvisited || !(imalloc != PetscTrMalloc || ifree != PetscTrFree), PETSC_COMM_SELF, PETSC_ERR_SUP, "cannot call multiple times");
212   PetscTrMalloc         = imalloc;
213   PetscTrFree           = ifree;
214   PetscTrRealloc        = iralloc;
215   petscsetmallocvisited = PETSC_TRUE;
216   PetscFunctionReturn(0);
217 }
218 
219 /*@C
220    PetscMallocClear - Resets the routines used to do mallocs and frees to the defaults.
221 
222    Not Collective
223 
224    Level: developer
225 
226    Notes:
227     In general one should never run a PETSc program with different malloc() and
228     free() settings for different parts; this is because one NEVER wants to
229     free() an address that was malloced by a different memory management system
230 
231     Called in PetscFinalize() so that if PetscInitialize() is called again it starts with a fresh slate of allocation information
232 
233 @*/
234 PetscErrorCode PetscMallocClear(void) {
235   PetscFunctionBegin;
236   PetscTrMalloc         = PetscMallocAlign;
237   PetscTrFree           = PetscFreeAlign;
238   PetscTrRealloc        = PetscReallocAlign;
239   petscsetmallocvisited = PETSC_FALSE;
240   PetscFunctionReturn(0);
241 }
242 
243 PetscErrorCode PetscMemoryTrace(const char label[]) {
244   PetscLogDouble        mem, mal;
245   static PetscLogDouble oldmem = 0, oldmal = 0;
246 
247   PetscFunctionBegin;
248   PetscCall(PetscMemoryGetCurrentUsage(&mem));
249   PetscCall(PetscMallocGetCurrentUsage(&mal));
250 
251   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));
252   oldmem = mem;
253   oldmal = mal;
254   PetscFunctionReturn(0);
255 }
256 
257 static PetscErrorCode (*PetscTrMallocOld)(size_t, PetscBool, int, const char[], const char[], void **) = PetscMallocAlign;
258 static PetscErrorCode (*PetscTrReallocOld)(size_t, int, const char[], const char[], void **)           = PetscReallocAlign;
259 static PetscErrorCode (*PetscTrFreeOld)(void *, int, const char[], const char[])                       = PetscFreeAlign;
260 
261 /*@C
262    PetscMallocSetDRAM - Set PetscMalloc to use DRAM.
263      If memkind is available, change the memkind type. Otherwise, switch the
264      current malloc and free routines to the PetscMallocAlign and
265      PetscFreeAlign (PETSc default).
266 
267    Not Collective
268 
269    Level: developer
270 
271    Notes:
272      This provides a way to do the allocation on DRAM temporarily. One
273      can switch back to the previous choice by calling PetscMallocReset().
274 
275 .seealso: `PetscMallocReset()`
276 @*/
277 PetscErrorCode PetscMallocSetDRAM(void) {
278   PetscFunctionBegin;
279   if (PetscTrMalloc == PetscMallocAlign) {
280 #if defined(PETSC_HAVE_MEMKIND)
281     previousmktype = currentmktype;
282     currentmktype  = PETSC_MK_DEFAULT;
283 #endif
284   } else {
285     /* Save the previous choice */
286     PetscTrMallocOld  = PetscTrMalloc;
287     PetscTrReallocOld = PetscTrRealloc;
288     PetscTrFreeOld    = PetscTrFree;
289     PetscTrMalloc     = PetscMallocAlign;
290     PetscTrFree       = PetscFreeAlign;
291     PetscTrRealloc    = PetscReallocAlign;
292   }
293   PetscFunctionReturn(0);
294 }
295 
296 /*@C
297    PetscMallocResetDRAM - Reset the changes made by PetscMallocSetDRAM
298 
299    Not Collective
300 
301    Level: developer
302 
303 .seealso: `PetscMallocSetDRAM()`
304 @*/
305 PetscErrorCode PetscMallocResetDRAM(void) {
306   PetscFunctionBegin;
307   if (PetscTrMalloc == PetscMallocAlign) {
308 #if defined(PETSC_HAVE_MEMKIND)
309     currentmktype = previousmktype;
310 #endif
311   } else {
312     /* Reset to the previous choice */
313     PetscTrMalloc  = PetscTrMallocOld;
314     PetscTrRealloc = PetscTrReallocOld;
315     PetscTrFree    = PetscTrFreeOld;
316   }
317   PetscFunctionReturn(0);
318 }
319 
320 static PetscBool petscmalloccoalesce =
321 #if defined(PETSC_USE_MALLOC_COALESCED)
322   PETSC_TRUE;
323 #else
324   PETSC_FALSE;
325 #endif
326 
327 /*@C
328    PetscMallocSetCoalesce - Use coalesced malloc when allocating groups of objects
329 
330    Not Collective
331 
332    Input Parameters:
333 .  coalesce - PETSC_TRUE to use coalesced malloc for multi-object allocation.
334 
335    Options Database Keys:
336 .  -malloc_coalesce - turn coalesced malloc on or off
337 
338    Note:
339    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.
340    This function can only be called immediately after PetscInitialize()
341 
342    Level: developer
343 
344 .seealso: `PetscMallocA()`
345 @*/
346 PetscErrorCode PetscMallocSetCoalesce(PetscBool coalesce) {
347   PetscFunctionBegin;
348   petscmalloccoalesce = coalesce;
349   PetscFunctionReturn(0);
350 }
351 
352 /*@C
353    PetscMallocA - Allocate and optionally clear one or more objects, possibly using coalesced malloc
354 
355    Not Collective
356 
357    Input Parameters:
358 +  n - number of objects to allocate (at least 1)
359 .  clear - use calloc() to allocate space initialized to zero
360 .  lineno - line number to attribute allocation (typically __LINE__)
361 .  function - function to attribute allocation (typically PETSC_FUNCTION_NAME)
362 .  filename - file name to attribute allocation (typically __FILE__)
363 -  bytes0 - first of n object sizes
364 
365    Output Parameters:
366 .  ptr0 - first of n pointers to allocate
367 
368    Notes:
369    This function is not normally called directly by users, but rather via the macros PetscMalloc1(), PetscMalloc2(), or PetscCalloc1(), etc.
370 
371    Level: developer
372 
373 .seealso: `PetscMallocAlign()`, `PetscMallocSet()`, `PetscMalloc1()`, `PetscMalloc2()`, `PetscMalloc3()`, `PetscMalloc4()`, `PetscMalloc5()`, `PetscMalloc6()`, `PetscMalloc7()`, `PetscCalloc1()`, `PetscCalloc2()`, `PetscCalloc3()`, `PetscCalloc4()`, `PetscCalloc5()`, `PetscCalloc6()`, `PetscCalloc7()`, `PetscFreeA()`
374 @*/
375 PetscErrorCode PetscMallocA(int n, PetscBool clear, int lineno, const char *function, const char *filename, size_t bytes0, void *ptr0, ...) {
376   va_list Argp;
377   size_t  bytes[8], sumbytes;
378   void  **ptr[8];
379   int     i;
380 
381   PetscFunctionBegin;
382   PetscCheck(n <= 8, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Attempt to allocate %d objects but only 8 supported", n);
383   bytes[0] = bytes0;
384   ptr[0]   = (void **)ptr0;
385   sumbytes = (bytes0 + PETSC_MEMALIGN - 1) & ~(PETSC_MEMALIGN - 1);
386   va_start(Argp, ptr0);
387   for (i = 1; i < n; i++) {
388     bytes[i] = va_arg(Argp, size_t);
389     ptr[i]   = va_arg(Argp, void **);
390     sumbytes += (bytes[i] + PETSC_MEMALIGN - 1) & ~(PETSC_MEMALIGN - 1);
391   }
392   va_end(Argp);
393   if (petscmalloccoalesce) {
394     char *p;
395     PetscCall((*PetscTrMalloc)(sumbytes, clear, lineno, function, filename, (void **)&p));
396     if (p == NULL) {
397       for (i = 0; i < n; i++) *ptr[i] = NULL;
398     } else {
399       for (i = 0; i < n; i++) {
400         *ptr[i] = bytes[i] ? p : NULL;
401         p       = (char *)PetscAddrAlign(p + bytes[i]);
402       }
403     }
404   } else {
405     for (i = 0; i < n; i++) PetscCall((*PetscTrMalloc)(bytes[i], clear, lineno, function, filename, (void **)ptr[i]));
406   }
407   PetscFunctionReturn(0);
408 }
409 
410 /*@C
411    PetscFreeA - Free one or more objects, possibly allocated using coalesced malloc
412 
413    Not Collective
414 
415    Input Parameters:
416 +  n - number of objects to free (at least 1)
417 .  lineno - line number to attribute deallocation (typically __LINE__)
418 .  function - function to attribute deallocation (typically PETSC_FUNCTION_NAME)
419 .  filename - file name to attribute deallocation (typically __FILE__)
420 -  ptr0 ... - first of n pointers to free
421 
422    Note:
423    This function is not normally called directly by users, but rather via the macros PetscFree(), PetscFree2(), etc.
424 
425    The pointers are zeroed to prevent users from accidentally reusing space that has been freed.
426 
427    Level: developer
428 
429 .seealso: `PetscMallocAlign()`, `PetscMallocSet()`, `PetscMallocA()`, `PetscFree1()`, `PetscFree2()`, `PetscFree3()`, `PetscFree4()`, `PetscFree5()`, `PetscFree6()`, `PetscFree7()`
430 @*/
431 PetscErrorCode PetscFreeA(int n, int lineno, const char *function, const char *filename, void *ptr0, ...) {
432   va_list Argp;
433   void  **ptr[8];
434   int     i;
435 
436   PetscFunctionBegin;
437   PetscCheck(n <= 8, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Attempt to allocate %d objects but only up to 8 supported", n);
438   ptr[0] = (void **)ptr0;
439   va_start(Argp, ptr0);
440   for (i = 1; i < n; i++) ptr[i] = va_arg(Argp, void **);
441   va_end(Argp);
442   if (petscmalloccoalesce) {
443     for (i = 0; i < n; i++) { /* Find first nonempty allocation */
444       if (*ptr[i]) break;
445     }
446     while (--n > i) *ptr[n] = NULL;
447     PetscCall((*PetscTrFree)(*ptr[n], lineno, function, filename));
448     *ptr[n] = NULL;
449   } else {
450     while (--n >= 0) {
451       PetscCall((*PetscTrFree)(*ptr[n], lineno, function, filename));
452       *ptr[n] = NULL;
453     }
454   }
455   PetscFunctionReturn(0);
456 }
457