In the past year or so there has been discussion of using mmap to set a start address for the offset of object memory so that memory address swizzling does not occur. Also various operating systems vendors say using mmap to page in large files is faster than using fread, this in fact is true and noticeable on the iPhone.
This requires a change to VMMaker so that sqAllocateMemory can pass the file pointer and the header size, since in the sqAllocateMemory we need to figure out the file (image size) and also understand the headersize of the image file to return a proper address to work with.
#define sqAllocateMemory(x,y,f,headersize)
(a) map in the image file as MAP_PRIVATE (copy on write) at a set location, noting that on os-x mmap (file offset) does not allow for non- multiples of page size, so we return the offset into the file as the address. (b) map in the free space at the page boundary rounded up based on the file size, plus the set location. This should give two mmap memory regions side by side.
Obvious issues, if any mmap fails we exit. We cannot be sure what will happen for memory mapping in the future, should we attempt recover at some other start location?
Source code below for comment.
?bug memory refers to (startOfAnonymousMemory+headersize) yet calculation in sqGrowMemoryBy uses memory and gMaxHeapSize. lazy adding of extra page size to freeSpaceRoundedUpToPageSize is preventing disaster, proper fix is exercise for the reader...
extern usqInt gMaxHeapSize; static usqInt gHeapSize; static void *startOfmmapForANONMemory,*startOfmmapForImageFile; static size_t fileRoundedUpToPageSize,freeSpaceRoundedUpToPageSize;
/* compute the desired memory allocation */
extern unsigned char *memory;
usqInt sqGetAvailableMemory() { return gMaxHeapSize; }
usqInt sqAllocateMemoryMac(sqInt minHeapSize, sqInt *desiredHeapSize, FILE * f,usqInt headersize) { void *possibleLocation,*startOfAnonymousMemory; off_t fileSize; struct stat sb; size_t pageSize= getpagesize(); size_t pageMask= ~(pageSize - 1); #define valign(x) ((x) & pageMask) #pragma unused(minHeapSize,desiredHeapSize) possibleLocation = 500*1024*1024; gHeapSize = gMaxHeapSize;
/* Lets see about mmap the image file into a chunk of memory at the 500MB boundary rounding up to the page size Then we on the next page anonymously allocate the required free space for young space*/ /* Thanks to David Pennell for suggesting this */ fstat(fileno((FILE *)f), &sb); fileSize = sb.st_size; fileRoundedUpToPageSize = valign(fileSize+pageSize-1); startOfAnonymousMemory = (void *) ((size_t) fileRoundedUpToPageSize + (size_t) possibleLocation); startOfmmapForImageFile = mmap(possibleLocation, fileRoundedUpToPageSize, PROT_READ|PROT_WRITE, MAP_FILE| MAP_PRIVATE,fileno((FILE *)f), 0); if (startOfmmapForImageFile != possibleLocation) { fprintf(stderr, "errno %d\n", errno); perror("startOfmmapForImageFile failed"); exit(42); } freeSpaceRoundedUpToPageSize = valign(gMaxHeapSize)- fileRoundedUpToPageSize+pageSize; startOfmmapForANONMemory = mmap(startOfAnonymousMemory, freeSpaceRoundedUpToPageSize, PROT_READ|PROT_WRITE, MAP_ANON| MAP_SHARED,0,0); if (startOfmmapForANONMemory != startOfAnonymousMemory) { fprintf(stderr, "errno %d\n", errno); perror("startOfmmapForANONMemory failed"); exit(42); } return (usqInt) startOfmmapForImageFile+headersize; }
sqInt sqGrowMemoryBy(sqInt memoryLimit, sqInt delta) { if ((usqInt) memoryLimit + (usqInt) delta - (usqInt) memory > gMaxHeapSize) return memoryLimit;
gHeapSize += delta; return memoryLimit + delta; }
sqInt sqShrinkMemoryBy(sqInt memoryLimit, sqInt delta) { returnsqGrowMemoryBy(memoryLimit,0-delta); }
sqInt sqMemoryExtraBytesLeft(sqInt flag) { return gMaxHeapSize - gHeapSize; }
void sqMacMemoryFree() { if (memory == NULL) return; munmap(startOfmmapForImageFile,fileRoundedUpToPageSize); munmap(startOfmmapForANONMemory,freeSpaceRoundedUpToPageSize); memory = NULL; }