Spooled temporary files
You can simplify the distinction by saying that a SpooledTemporaryFile is a piece of memory that pretends to be a file (until it starts to be a file), while a memory mapped file is a file pretending to be a piece of memory.
The point of a SpooledTemporaryFile is that you may not know a-priori whether the data is large enough to justify writing a temporary file. For example if you handle images, they might be tiny thumbnails or large HD images. If the data turns out to be tiny, the overhead of handling actual files outweighs the benefits, so the spooled file gives you a handy abstraction handling both cases.
This also means there is no point in using them compared to a regular temporary file if you want to memory-map it anyway. At that point it needs to have been written to disk.
Memory mapping
Memory mapping on the other hand works through the page cache (at least that's what it's called on Linux, I'm not sure if Windows uses the same name but they have the same mechanism). The OS uses memory that is not currently used for anything else to cache its disk content. When you read or write a file, the OS normally just copies between your read/write buffer and the page cache; and then of course it schedules the actual write to disk from the page cache or the read to fill the page cache with the requested data.
When you memory-map, the memory pages in the page cache are mapped into the address space of your application so that you can directly access them. When you try to read a page that is not currently in the cache, it will cause an access fault by the CPU. Then the OS takes over and resolves that fault by loading the page in. Conversely, when you write to a page, the OS will track that the page was "dirtied" and write it back to disk at some point. When the OS needs to make room in the page cache, it will pick some pages, make sure that their on-disk representation is consistent, and then drop the page. The next access to that page through mapped memory will then cause another access fault and the whole thing repeats.
It is interesting to note that the same way a memory-mapped file is backed by its on-disk file, regular anonymous memory is backed by the swap file. It's practically the same mechanism: If you access a piece of memory that was swapped out, it faults and the OS reloads the page from swap. And if the OS runs out of memory, it picks pages to move into swap storage.
Comparison
This means that all options will lead the OS to the same mechanism for limiting memory use: If you keep it in normal memory, the OS may swap it out, if you put it into a temporary file, the OS will move it out of page cache, and if you memory map it, it will do the same. However, there are some differences:
- The OS prioritizes anonymous memory over page cache and is more likely to evict page cache pages than normal memory. On Linux you can control this via
/proc/sys/vm/swappiness. This also means that keeping data in memory may make the system perform worse as the page cache shrinks
- The OS may not be able to swap if none is configured. Then the OS will just start killing memory-hungry processes
- The OS typically doesn't know that you write a temporary file. So it writes to disk very quickly and doesn't keep the data purely in memory for crash safety. Therefore a regular temporary file will often result in disk IO even if your use is brief. Also, writing to the page cache is often artificially rate-limited to match the disk performance
- At least on Linux it is not uncommon for the
/tmp directory to be a tmpfs ram-disk. So you are really just writing to an in-memory buffer. But this is something the system admin controls and presumably enabled knowing their system has enough RAM to handle it
- Memory mapping can have worse performance in sequential accesses compared to normal IO. It can lead to excessive numbers of access faults. Part of the issue is that all the OS sees is that you wanted to access a 4 kiB page. It doesn't know how much data you want to read/write overall. I've seen it absolutely tank performance for large streaming I/O. It's much more suitable for random or repeated access
- On the other hand, for these repeated accesses, it avoids the cost of doing system calls
- It also moves the burden of figuring out what to keep in memory and what to store on disk to the OS which may or may not be a good thing depending on your skill for predicting this compared to the OS
Conclusion
It is hard to give good advise on what you should use. Much of it depends on your access pattern and your system. How much RAM do you have, how much do you need? How fast is your disk? Broadly speaking the order is temporary file -> memory mapping -> normal array going from most memory savings to highest effective performance, if used properly.