Memory Mapped files on iOS – worth checking out

2

Overview

I’ve been following Carmack’s tweets on porting RAGE to iOS and was intrigued by his and other mentions that memory mapped files work so well on the platform.

Mapped files have the advantage of being cached by the OS (in kernel memory space) and paged in and out of memory as necessary. On a desktop platform or console, the unpredictable seek time of the disk makes this less feasible, but with the gauranteed solid state disk of apple products it becomes an interesting proposition.

Since the mapping is in kernel space it can actually remain cached across application launches which is a nice performance win.

The Test

Wooords has a 1MB resident english dictionary for checking word submissions against. The dictionary is a array of sorted 64bit ints, to look up a word a simple binary search is performed.

The dictionary is an ideal candidate for moving to a memory mapped file as the OS can cache the commonly hit parts of the data file and leave the rest on disk (although I’m not sure how smart the caching algorithm is? Is it page based?)

To test these theories out I implemented the required calls and timed the binary search for a variety of words.

Malloc Mapped
Lookup 1 0.009ms 0.111ms
Lookup 2 0.009ms 0.010ms
Lookup 3 0.008ms 0.008ms
Lookup 4 0.011ms 0.011ms
Lookup 5 0.008ms 0.072ms

As we can see the mmap array has similar performance but there’s an initial spike as the data is loaded by the kernel and a couple smaller spikes during usage.

For dictionary lookup the extra latency now and again is not a problem and therefore the choice to use mapped files easy.

Technical Implementation

To use a memory mapped file, simply open the file as usual and then call the mmap function. Once you’re done with the data munmap it and close the file.

Map

FILE* fp = fopen("file.dat", "rb");
fseek(fp, 0, SEEK_END);
size_t length = ftell(fp);fseek(fp, 0, SEEK_SET);
int fd = fileno(fp);
off_t offset = 0;
void* data = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, offset);
fclose(fp);

Arguments

NULL Base address to use for mapping, this doesn’t matter for us, let the OS choose one
length Number of bytes to map into memory, in our case the whole file
PROT_READ We’re only interested in readying the file
MAP_SHARED Share this file with other processes on the system, if its written to we get the updates. This should be the most performant, otherwise the OS has to do a copy on write.
fd The file descriptor
offset The start of the mapping on the file

For a detailed explation of the parameters see man mmap.

Unmap

munmap(data, length);

Conclusion

Using memory mapped files can reduce your process memory footprint with minor modifications to your code. It can however cause small load stalls, so the best thing to do is to benchmark it in your application.

Overall its another great tool in the iOS developers toolbox.

Custom leaderboards with GoogleAppEngine

Google App EngineCustom? Why custom I hear you say - GameCenter provides all the leaderboard APIs anybody could ever need, or does it? Unfortunately Wooords requires a lot more than 25 (the current GameCenter limit) leaderboards...

READ MORE »

Using OpenAL on iOS

There are a number of articles around on implementing CoreAudio on iOS, but not so much information on OpenAL on this platform. In this article I'm going to give a brief overview of how and why I chose to use OpenAL. Why OpenAL? OpenAL provides...

READ MORE »

Building a MVC UI with libRocket

libRocket The Model-View-Controller pattern is used extensively in modern web APIs. (Rails, Django, ZendFramework, ...) so I thought I'd give it a try with the interface I'm building for my latest project. It...

READ MORE »

Building Universal i386/arm6/arm7 freetype library

Over the last couple of weeks I've been preparing for the libRocket open source release. One of the things that confused me for awhile was how to go about building FreeType for iPhone/iOS. After a fair bit of trial and error I came up...

READ MORE »