Skip to content

perf(fuse): set FOPEN_KEEP_CACHE on read-only mounts - #130

Merged
XciD merged 2 commits into
mainfrom
perf/keep-cache
Apr 26, 2026
Merged

XciD merged 2 commits into
mainfrom
perf/keep-cache

Conversation

@XciD

@XciD XciD commented Apr 26, 2026 •

Copy link
Copy Markdown
Member

Summary

Two-commit branch making the FUSE kernel page cache survive across opens:

  1. FOPEN_KEEP_CACHE per-file flag: tells the kernel to retain cached pages on open. Without it, every open() invalidates the file's pages and forces a full FUSE READ traversal even when bytes are already resident.

  2. FUSE_AUTO_INVAL_DATA capability: lets the kernel auto-invalidate cached pages on mtime/size changes, making KEEP_CACHE safe on writable mounts too.

Behavior

Mount mode Kernel ≥ 3.15 (AUTO_INVAL ok) Kernel < 3.15
--read-only KEEP_CACHE on KEEP_CACHE on
Writable KEEP_CACHE on KEEP_CACHE off (current main behavior)
--direct-io DIRECT_IO (KEEP_CACHE not applicable) same

Bench

m6i.xlarge, transformers from_pretrained on a 9.6 GiB safetensors mount, with the kernel page cache hot from a prior load:

before after
warm model load ~4.30 s ~0.81 s
peak RSS 1060 MB 1060 MB

The first cold load is unaffected — KEEP_CACHE only helps subsequent reads while the page cache survives. When the kernel cache is dropped (drop_caches, memory pressure, process restart), behavior reverts to the cold path.

Notes

  • 250 unit tests pass, no new tests (effect is end-to-end and was verified on EC2).
  • Read-only mode is the dominant case for HF Spaces (mounted model repos); writable mode is supported as a bonus when the kernel cooperates.
  • The auto_inval_supported flag is a one-shot atomic written during the FUSE init handshake, read on every open. No hot-path overhead.

By default, the FUSE kernel module invalidates the page cache for a
file on every open() — a conservative behavior because the FUSE server
might mutate the file out-of-band. For our read-only mount the
underlying view is stable for the lifetime of the session, so we can
ask the kernel to retain cached pages with FOPEN_KEEP_CACHE.

Without it, every from_pretrained re-walks the file via FUSE READ
calls, deserialising xorbs from the local cache for each tensor —
even when the bytes were already resident from the previous load.

Bench (m6i.xlarge, gemma-4-E2B-it 9.6 GiB safetensors, transformers
real load with low_cpu_mem_usage=False):

  | scenario          | before  | after   |
  |-------------------|---------|---------|
  | cold model load   | 28.28s  | 23.07s  |
  | warm model load   |  4.30s  |  0.81s  |
  | peak RSS          | 1060 MB | 1060 MB |

Warm reload is now within ~150 ms of pure-disk mmap (0.69s on a local
copy of the same model). The cold path also improves: pages fetched
during the initial walk stay resident for any re-touch within the
load.

Read-only check ensures we don't enable this on writable mounts where
out-of-band mutation is theoretically possible (the writable path
goes through staging anyway, but explicit is safer).

No new tests: behavior is observable end-to-end via the open path
and was verified on EC2 with a real transformers from_pretrained.
@github-actions

github-actions Bot commented Apr 26, 2026 •

Copy link
Copy Markdown
Contributor

Benchmark Results

============================================================
  Benchmark — 50MB
------------------------------------------------------------
  Metric                                 FUSE          NFS
  ------------------------------ ------------ ------------
  Sequential read                    208.6 MB/s     220.6 MB/s
  Sequential re-read                1918.4 MB/s    2111.5 MB/s
  Range read (1MB@25MB)                0.5 ms         0.2 ms
  Random reads (100x4KB avg)           0.0 ms         0.0 ms
  Sequential write (FUSE)           1248.3 MB/s
  Close latency (CAS+Hub)            0.267 s
  Write end-to-end                   162.7 MB/s
  Dedup write                       1420.5 MB/s
  Dedup close latency                0.147 s
  Dedup end-to-end                   275.2 MB/s
============================================================
============================================================
  Benchmark — 200MB
------------------------------------------------------------
  Metric                                 FUSE          NFS
  ------------------------------ ------------ ------------
  Sequential read                    828.6 MB/s     765.4 MB/s
  Sequential re-read                2123.5 MB/s    2267.9 MB/s
  Range read (1MB@25MB)                0.2 ms         0.3 ms
  Random reads (100x4KB avg)           0.0 ms         0.0 ms
  Sequential write (FUSE)            949.5 MB/s
  Close latency (CAS+Hub)            0.160 s
  Write end-to-end                   539.0 MB/s
  Dedup write                       1021.8 MB/s
  Dedup close latency                0.118 s
  Dedup end-to-end                   637.0 MB/s
============================================================
============================================================
  Benchmark — 500MB
------------------------------------------------------------
  Metric                                 FUSE          NFS
  ------------------------------ ------------ ------------
  Sequential read                   1246.4 MB/s     658.4 MB/s
  Sequential re-read                2077.1 MB/s    2402.7 MB/s
  Range read (1MB@25MB)                0.2 ms         0.2 ms
  Random reads (100x4KB avg)           0.0 ms         0.0 ms
  Sequential write (FUSE)            870.7 MB/s
  Close latency (CAS+Hub)            0.155 s
  Write end-to-end                   685.8 MB/s
  Dedup write                       1186.5 MB/s
  Dedup close latency                0.141 s
  Dedup end-to-end                   888.6 MB/s
============================================================
============================================================
  fio Benchmark Results
------------------------------------------------------------
  Job                        FUSE MB/s   NFS MB/s  FUSE IOPS   NFS IOPS
  ------------------------- ---------- ---------- ---------- ----------
  seq-read-100M                  515.5      480.8                      
  seq-reread-100M               2325.6       70.4                      
  rand-read-4k-100M                0.1        0.1         20         20
  seq-read-5x10M                 892.9      714.3                      
  rand-read-10x1M                  0.1        0.1         36         37
  Random Read Latency           FUSE avg      NFS avg
  ------------------------- ------------ ------------
  rand-read-4k-100M           49921.5 us   50240.1 us
  rand-read-10x1M             27906.6 us   26899.3 us
============================================================

@github-actions

Copy link
Copy Markdown
Contributor

POSIX Compliance (pjdfstest)

============================================================
  pjdfstest POSIX Compliance Results
------------------------------------------------------------
  Files: 130/130 passed    Tests: 832 total (0 subtests failed)
  Result: PASS
------------------------------------------------------------
  Category               Passed    Total   Status
  -------------------- -------- -------- --------
  chflags                     5        5       OK
  chmod                       8        8       OK
  chown                       6        6       OK
  ftruncate                  13       13       OK
  granular                    5        5       OK
  mkdir                       9        9       OK
  open                       19       19       OK
  posix_fallocate             1        1       OK
  rename                     10       10       OK
  rmdir                      11       11       OK
  symlink                    10       10       OK
  truncate                   13       13       OK
  unlink                     11       11       OK
  utimensat                   9        9       OK
============================================================

@XciD
XciD force-pushed the perf/keep-cache branch 2 times, most recently from 479dc77 to 38eb3fc Compare April 26, 2026 06:43
Request FUSE_AUTO_INVAL_DATA in init() and gate FOPEN_KEEP_CACHE on
either read-only or auto_inval-supported. Read-only mounts already get
the cache benefit; writable mounts now get it too on kernels that
support the capability (Linux 3.15+), with the kernel auto-invalidating
cached pages whenever a file's mtime/size changes.

If the kernel lacks AUTO_INVAL_DATA on a writable mount we skip
KEEP_CACHE there (current main behavior), so worst case is a no-op.
@XciD
XciD force-pushed the perf/keep-cache branch from 38eb3fc to 6c16eb5 Compare April 26, 2026 07:09
@XciD
XciD marked this pull request as ready for review April 26, 2026 07:11
@XciD
XciD merged commit bc0c33f into main Apr 26, 2026
6 checks passed
@XciD
XciD deleted the perf/keep-cache branch April 26, 2026 07:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant