mac: implement (file) cache invalidation
authorSitsofe Wheeler <sitsofe@yahoo.com>
Mon, 25 Aug 2025 21:38:27 +0000 (22:38 +0100)
committerSitsofe Wheeler <sitsofe@yahoo.com>
Wed, 3 Sep 2025 19:26:27 +0000 (20:26 +0100)
commit41880b9b7aa4db7d537e096e15192437a990d9f1
tree29cfbb70e19df84544949dbc140330c4be191740
parentd6acda3d2e3992dbbb51e6ce2ba5d69e5ef85570
mac: implement (file) cache invalidation

This (finally) provides macOS cache invalidation and is heavily based on
code originally provided by DeveloperEcosystemEngineering@apple.

Because posix_fadvise() isn't implemented on macOS,
DeveloperEcosystemEngineering demonstrated that creating a shared
mapping of a file and using using msync([...], MS_INVALIDATE) on it can
be used to discard covered page cache pages instead - ingenious! This
commit uses that technique to create a macOS posix_fadvise([...],
POSIX_FADV_DONTNEED) shim.

To paraphrase commit 8300eba5 ("windowsaio: add best effort cache
invalidation") that was done for similar reasons:

This change may make default bandwidth speeds on macOS look lower
compared to older versions of fio but this matches the behaviour of fio
on other platforms with invalidation (such as Linux) because we are
trying to avoid measuring cache reuse (unless invalidate=0 is set).

The impact of invalidation is demonstrated by the bandwidths achieved by
the following jobs running on an SSD of an otherwise idle Intel Mac
laptop with 16GBytes of RAM:

./fio --stonewall --size=128M --ioengine=posixaio --filename=fio.tmp \
  --iodepth=64 --bs=4k --direct=0 \
  --name=create --rw=write \
  --name=cached --rw=randread --loops=2 --invalidate=0 \
  --name=invalidated --rw=randread --loops=2 --invalidate=1

[...]
cached: (groupid=1, jobs=1): err= 0: pid=7795: Tue Sep  2 22:34:12 2025
  read: IOPS=228k, BW=889MiB/s (932MB/s)(256MiB/288msec)
[...]
invalidated: (groupid=2, jobs=1): err= 0: pid=7796: Tue Sep  2 22:34:12 2025
  read: IOPS=46.8k, BW=183MiB/s (192MB/s)(256MiB/1399msec)

v2:
- Move platform specific code into its own file under os/mac/
- Don't do prior fsync() because msync([...], MS_INVALIDATE) doesn't
  imply the dropping of dirty pages and will have the same effect

v3:
- Up the mmap chunk size to 16 GBytes to reduce the number of times we
  mmap()/msync()/munmap() on large files
- Align offset and len to the system page size to prevent errors on jobs
  like ./fio --name=n --offset=2k --size=30k
- Try and munmap() if msync() fails
- Make Rosetta comment clearer
- Drop some variables and rename some others
- Don't bother trying to restore errno after displaying an error message
  because posix_fadvise() isn't defined as setting errno

Fixes: https://github.com/axboe/fio/issues/48
Suggested-by: DeveloperEcosystemEngineering <DeveloperEcosystemEngineering@apple.com>
Signed-off-by: Sitsofe Wheeler <sitsofe@yahoo.com>
Makefile
os/mac/posix.c [new file with mode: 0644]
os/mac/posix.h [new file with mode: 0644]
os/os-mac.h