You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
8.9 KiB
245 lines
8.9 KiB
/*
|
|
* Copyright(C) 1999-2021 National Technology & Engineering Solutions
|
|
* of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
|
|
* NTESS, the U.S. Government retains certain rights in this software.
|
|
*
|
|
* See packages/seacas/LICENSE for details
|
|
*/
|
|
/*
|
|
*/
|
|
|
|
/*
|
|
* NOTES ON MODULE EXMEMY:
|
|
*
|
|
* -2. The big assumption on which this is based is that a FTNINT
|
|
* which is either an int or a long int, is the same size as
|
|
* the void* returned by malloc. There are both compile-time
|
|
* and run-time assertions that attempt to catch this.
|
|
*
|
|
* -1. The information below is the original implementation. At that
|
|
* time, the only C routines linked with most executables were the
|
|
* supes library itself and there were no other routines calling
|
|
* 'sbrk' and life was good. This situation has changed with the
|
|
* addition of the exodusII, netcdf, nemesis, and other libraries.
|
|
* Some of these are C-based and do 'malloc' and 'free'
|
|
* calls. Most man pages for 'malloc' and 'sbrk' warn of dire
|
|
* consequences of mixing calls to 'malloc' and 'sbrk' and we have
|
|
* had instances of memory corruption under the old model. To
|
|
* eliminate this undefined and undesirable behavior, the EXMEMY
|
|
* routine has been rewritten to use 'malloc' and 'free' instead
|
|
* of 'sbrk' calls. The PAGESIZE manipulations have also been
|
|
* removed since the underlying 'malloc' implementation is better
|
|
* at dealing with this than we are (hopefully).
|
|
*
|
|
* The implementation of 'free' is somewhat of a kluge and
|
|
* required changes to the calling 'mem_mgr' routines. Specifically,
|
|
*
|
|
* 1) A few routines were disabled -- mdget which is supposed to
|
|
* get a large block of memory and parcel it out in smaller chunks.
|
|
* This minimizes system calls, but is incompatible with 'free' since
|
|
* memory passed to 'mddel' may not be on a boundary obtained
|
|
* my malloc.
|
|
*
|
|
* 2) 'mdlong' is mapped to 'realloc'. A flag is passed into the
|
|
* exmemy call which tells whether we are freeing memory or
|
|
* changing size. The '*memret' argument is used to pass the flag
|
|
* in to avoid changing the interface to exmemy.
|
|
*
|
|
* 3) 'deferred' mode is eliminated; all memory requests are
|
|
* satisfied immediately.
|
|
*
|
|
* 4) 'mdcomp' is also eliminated.
|
|
*
|
|
* The interface has not been changed; the disabled calls are
|
|
* just noops.
|
|
*
|
|
* ---Below here is historical notes---
|
|
*
|
|
* 0. On a request for more memory, we ask for it in multiples of
|
|
* PAGES of memory of size PAGESIZE. The reason is that on allocation,
|
|
* the memory manager does keep track the voids in memory. Unfortunately,
|
|
* the same cannot be said for the voids created on deallocation, so...
|
|
*
|
|
* 1. The SUPES memory handler, EXMEMY, normally deallocates memory
|
|
* only if one asks for it a a location that is an exact page multiple.
|
|
* Further, the memory manager used to try to release memory
|
|
* from each memory block---this, of course, assumes that the OS
|
|
* allows for gaps in a users data space. It so happens that VMS
|
|
* does. I've changed the memory manager (cf. mem_mgr/mxgive) so that
|
|
* it now only tries to release a void if it is at the top of the
|
|
* program space. I release as much as I can on any given release
|
|
* request.
|
|
*
|
|
* 2. Although it's not clear from the comments, or from the documentation
|
|
* that I've read, on deallocation *memrtn is the amount of memory that
|
|
* exmemy was *NOT* able to return to the system. Still unclear?
|
|
* An example: Suppose that I tried to return 524 bytes to the system.
|
|
* Ultimately, I would do this through a call to exmemy with a value
|
|
* for *memreq of -524 / Numsize (remember we ask to give in numeric
|
|
* storage units). Assuming that the amount actually returned
|
|
* was 512 then *memrtn should be set to 12 divided by the NumSize
|
|
* for most systems---see the comments below for the Cray strategy.
|
|
* (memrtn is now the amount of memory released/allocated. If it is
|
|
* less than zero, an error occurred)
|
|
*/
|
|
|
|
/*
|
|
* Assumptions:
|
|
* 1. All systems have stdlib.h and define the prototype for malloc
|
|
* 2. All systems are ANSI C compliant (have function prototypes)
|
|
* 3. sizeof(FTNINT) == sizeof(void*)
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
/*
|
|
* Define the Fortran/C interface for the system in question
|
|
* See also itools/config/cf/fortranc.h, which defaults to float, int --pw
|
|
*/
|
|
|
|
#include <fortranc.h>
|
|
|
|
#define CT_ASSERT(e) extern char(*ct_assert(void))[sizeof(char[1 - 2 * !(e)])]
|
|
/* If the following line causes a compile-time error, then there is a problem
|
|
* which will cause the supes memory manager to not work correctly on this
|
|
* platform. The error will be something similar to:
|
|
*
|
|
* exmemy.c(141): error: the size of an array must be greater than zero
|
|
* CT_ASSERT(sizeof(FTNINT) == sizeof(void*));
|
|
*
|
|
* Contact Greg Sjaardema, gdsjaar@sandia.gov for asisstance.
|
|
*/
|
|
CT_ASSERT(sizeof(FTNINT) == sizeof(void *));
|
|
|
|
#if defined(ADDC_)
|
|
void exmemy_(FTNINT *memreq, FTNINT *locblk, FTNINT *memrtn)
|
|
#else
|
|
void exmemy(FTNINT *memreq, FTNINT *locblk, FTNINT *memrtn)
|
|
#endif /* ADDC_ */
|
|
{
|
|
size_t numbytes;
|
|
size_t NumSize; /* Get multiplier which allows us to */
|
|
/* convert everything in terms of Numeric Storage Units. */
|
|
NumSize = sizeof(FTNREAL) / sizeof(char);
|
|
|
|
/* Normal allocation call */
|
|
if (*memreq > 0) { /* Then we need more memory. */
|
|
numbytes = (*memreq) * NumSize;
|
|
|
|
size_t *block_location = (size_t *)malloc(numbytes);
|
|
|
|
/* printf("%11x %11d\n", block_location, numbytes); */
|
|
/* convert back to numerical storage units */
|
|
*locblk = (FTNINT)((size_t)block_location / NumSize);
|
|
|
|
/* See if we have lost any information in the conversion. For example, if
|
|
* the pointer is a 64-bit quantity and a FTNINT is 32-bits, we may not be
|
|
* able to recover the block_location... This should have been caught in
|
|
* the ct_assert above, but if not, we check again here...
|
|
*/
|
|
assert(block_location == (size_t *)((size_t)(*locblk) * NumSize));
|
|
|
|
if (block_location == NULL) {
|
|
/*
|
|
* Then the call to 'malloc' has failed, most likely due to
|
|
* asking for more memory than what's available.
|
|
*/
|
|
*memrtn = -1;
|
|
}
|
|
else {
|
|
/* The last call for memory was successful! */
|
|
*memrtn = (FTNINT)(numbytes / NumSize);
|
|
}
|
|
}
|
|
else {
|
|
/* Otherwise, if *memrtn is negative, we want to give some back. */
|
|
|
|
/*
|
|
* There are a few kludges used here to try to get the memory
|
|
* system to work correctly with malloc/free instead of sbrk.
|
|
* A flag is passed in via the memrtn variable:
|
|
* -999 == free the memory block
|
|
* -998 == realloc
|
|
*/
|
|
|
|
/* Adjust '*locblk' to be the raw address. It is passed
|
|
* in in terms of Numeric Storage Units.
|
|
*/
|
|
size_t *block_location = (size_t *)((size_t)(*locblk) * NumSize);
|
|
|
|
if (*memrtn == -999 || *memreq == 0) {
|
|
/* Handle normal 'free' */
|
|
/* printf("FREE: %11x %11d\n", block_location, *memreq); */
|
|
free(block_location);
|
|
*memrtn = -(*memreq);
|
|
}
|
|
else if (*memrtn == -998) {
|
|
/* realloc call to shrink/grow memory */
|
|
numbytes = -(*memreq) * NumSize;
|
|
/* printf("PRE: %11x %11d (realloc)\n", block_location, numbytes); */
|
|
size_t *new_location = realloc(block_location, numbytes);
|
|
/* printf("POST: %11x %11d\n", new_location, numbytes); */
|
|
|
|
if (new_location == NULL && *memreq > 0) {
|
|
/*
|
|
* Then the call to 'realloc' has failed, most likely due to
|
|
* asking for more memory than what's available.
|
|
*/
|
|
*memrtn = -1;
|
|
}
|
|
else {
|
|
*memrtn = -(*memreq);
|
|
|
|
/* convert back to numerical storage units */
|
|
*locblk = (FTNINT)((size_t)new_location / NumSize);
|
|
}
|
|
/* If '*memrtn' flag was not set to -999 or -998,
|
|
* ignore free request */
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef TEST
|
|
#include <stdio.h>
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
FTNINT locblk;
|
|
FTNINT memrtn;
|
|
size_t first = 0;
|
|
size_t last = 0;
|
|
|
|
assert(sizeof(FTNREAL) == sizeof(FTNINT));
|
|
|
|
printf("Size of FORTRAN int/real = %lu bytes\n", sizeof(FTNINT));
|
|
|
|
FTNINT memreq = 10000000; /* 10 Million words */
|
|
for (int i = 0; i < 1000; i++) {
|
|
#if defined(ADDC_)
|
|
exmemy_(&memreq, &locblk, &memrtn);
|
|
#else
|
|
exmemy(&memreq, &locblk, &memrtn);
|
|
#endif /* ADDC_ */
|
|
printf("%4d: %11x %11d\n", i, locblk, memrtn);
|
|
|
|
if (memrtn < 0) {
|
|
FTNINT allocated;
|
|
FTNINT overhead;
|
|
allocated = i * (memreq / 1000000);
|
|
overhead = (last - first - (i - 1) * memreq) * sizeof(FTNINT);
|
|
printf("Total Allocation = %d MegaWords, %d MB\n", allocated, allocated * sizeof(FTNINT));
|
|
|
|
/* NOTE: 'last' is pointer to beginning of last block, not end */
|
|
printf("Overhead = %d bytes, %d bytes/allocation\n", overhead, overhead / (i - 1));
|
|
|
|
break;
|
|
}
|
|
else {
|
|
last = locblk;
|
|
}
|
|
if (i == 0) {
|
|
first = locblk;
|
|
}
|
|
}
|
|
}
|
|
#endif /* TEST */
|
|
|