Improving disk cache performance in general is more than just increasing the file system cache size unless your whole system fits in RAM in which case you should use RAM drive (tmpfs
is good because it allows falling back to disk if you need the RAM in some case) for runtime storage (and perhaps an initrd script to copy system from storage to RAM drive at startup).
You didn't tell if your storage device is SSD or HDD. Here's what I've found to work for me (in my case sda
is a HDD mounted at /home
and sdb
is SSD mounted at /
).
First optimize the load-stuff-from-storage-to-cache part:
Here's my setup for HDD (make sure AHCI+NCQ is enabled in BIOS if you have toggles):
echo cfq > /sys/block/sda/queue/scheduler
echo 10000 > /sys/block/sda/queue/iosched/fifo_expire_async
echo 250 > /sys/block/sda/queue/iosched/fifo_expire_sync
echo 80 > /sys/block/sda/queue/iosched/slice_async
echo 1 > /sys/block/sda/queue/iosched/low_latency
echo 6 > /sys/block/sda/queue/iosched/quantum
echo 5 > /sys/block/sda/queue/iosched/slice_async_rq
echo 3 > /sys/block/sda/queue/iosched/slice_idle
echo 100 > /sys/block/sda/queue/iosched/slice_sync
hdparm -q -M 254 /dev/sda
Worth noting for the HDD case is high fifo_expire_async
(usually write) and long slice_sync
to allow a single process to get high throughput (set slice_sync
to lower number if you hit situations where multiple processes are waiting for some data from the disk in parallel). The slice_idle
is always a compromise for HDDs but setting it somewhere in range 3-20 should be okay depending on disk usage and disk firmware. I prefer to target for low values but setting it too low will destroy your throughput. The quantum
setting seems to affect throughput a lot but try to keep this as low as possible to keep latency on sensible level. Setting quantum
too low will destroy throughput. Values in range 3-8 seem to work well with HDDs. The worst case latency for a read is (quantum
* slice_sync
) + (slice_async_rq
* slice_async
) ms if I've understood the kernel behavior correctly. The async is mostly used by writes and since you're willing to delay writing to disk, set both slice_async_rq
and slice_async
to very low numbers. However, setting slice_async_rq
too low value may stall reads because writes cannot be delayed after reads any more. My config will try to write data to disk at most after 10 seconds after data has been passed to kernel but since you can tolerate loss of data on power loss also set fifo_expire_async
to 3600000
to tell that 1 hour is okay for the delay to disk. Just keep the slice_async
low, though, because otherwise you can get high read latency.
The hdparm
command is required to prevent AAM from killing much of the performance that AHCI+NCQ allows. If your disk makes too much noise, then skip this.
Here's my setup for SSD (Intel 320 series):
echo cfq > /sys/block/sdb/queue/scheduler
echo 1 > /sys/block/sdb/queue/iosched/back_seek_penalty
echo 10000 > /sys/block/sdb/queue/iosched/fifo_expire_async
echo 20 > /sys/block/sdb/queue/iosched/fifo_expire_sync
echo 1 > /sys/block/sdb/queue/iosched/low_latency
echo 6 > /sys/block/sdb/queue/iosched/quantum
echo 2 > /sys/block/sdb/queue/iosched/slice_async
echo 10 > /sys/block/sdb/queue/iosched/slice_async_rq
echo 1 > /sys/block/sdb/queue/iosched/slice_idle
echo 20 > /sys/block/sdb/queue/iosched/slice_sync
Here it's worth noting the low values for different slice settings. The most important setting for an SSD is slice_idle
which must be set to 0-1. Setting it to zero moves all ordering decisions to native NCQ while setting it to 1 allows kernel to order requests (but if the NCQ is active, the hardware may override kernel ordering partially). Test both values to see if you can see the difference. For Intel 320 series, it seems that setting slide_idle
to 0
gives the best throughput but setting it to 1
gives best (lowest) overall latency. If you have recent enough kernel, you can use slide_idle_us
to set the value in microseconds instead of milliseconds and you could use something like echo 14 > slice_idle_us
instead. Suitable value seems to be close to 700000 divided by max practical IOPS your storage device can support so 14 is okay for pretty fast SSD devices.
For more information about these tunables, see https://www.kernel.org/doc/Documentation/block/cfq-iosched.txt .
Update in year 2020 and kernel version 5.3 (cfq is dead):
#!/bin/bash
modprobe bfq
for d in /sys/block/sd?; do
# HDD (tuned for Seagate SMR drive)
echo bfq >"$d/queue/scheduler"
echo 4 >"$d/queue/nr_requests"
echo 32000 >"$d/queue/iosched/back_seek_max"
echo 3 >"$d/queue/iosched/back_seek_penalty"
echo 80 >"$d/queue/iosched/fifo_expire_sync"
echo 1000 >"$d/queue/iosched/fifo_expire_async"
echo 5300 >"$d/queue/iosched/slice_idle_us"
echo 1 >"$d/queue/iosched/low_latency"
echo 200 >"$d/queue/iosched/timeout_sync"
echo 0 >"$d/queue/iosched/max_budget"
echo 1 >"$d/queue/iosched/strict_guarantees"
# additional tweaks for SSD (tuned for Samsung EVO 850):
if test $(cat "$d/queue/rotational") = "0"; then
echo 36 >"$d/queue/nr_requests"
echo 1 >"$d/queue/iosched/back_seek_penalty"
# slice_idle_us should be ~ 0.7/IOPS in µs
echo 16 >"$d/queue/iosched/slice_idle_us"
echo 10 >"$d/queue/iosched/fifo_expire_sync"
echo 250 >"$d/queue/iosched/fifo_expire_async"
echo 10 >"$d/queue/iosched/timeout_sync"
echo 0 >"$d/queue/iosched/strict_guarantees"
fi
done
The setup is pretty similar but I now use bfq
instead of cfq
because the latter is not available with modern kernels. I try to keep nr_requests
as low as possible to allow bfq
to control the scheduling more accurately. At least Samsung SSD drives seem to require a pretty deep queue to be able to run with high IOPS. Update: Many Samsung SSDs have a firmware bug and can hang the whole device if nr_requests
is too high and OS submits lots of requests rapidly. I've seen random freeze about once every 2 months if I use high nr_requests
(e.g. 32 or 36), but the value 6
has been stable this far. The official fix is to set it to 1
but it hurts the performance a lot! For more details, see https://bugzilla.kernel.org/show_bug.cgi?id=203475 and https://bugzilla.kernel.org/show_bug.cgi?id=201693 – basically, if you have a Samsung SSD device and see failed command: WRITE FPDMA QUEUED
in the kernel log, you've been bitten by this bug.
I'm using Ubuntu 18.04 with kernel package linux-lowlatency-hwe-18.04-edge
which has bfq
only as a module so I need to load it before being able to switch to it.
I also nowadays also use zram
but I only use 5% of RAM for zram. This allows Linux kernel to use swapping related logic without touching the disks. However, if you decide to go with zero disk swap, make sure your apps do not leak RAM or you're wasting money.
Now that we have configured kernel to load stuff from disk to cache with sensible performance, it's time to adjust the cache behavior:
According to benchmarks I've done, I wouldn't bother setting read ahead via blockdev
at all. Kernel default settings are fine.
Set system to prefer swapping file data over application code (this does not matter if you have enough RAM to keep whole filesystem and all the application code and all virtual memory allocated by applications in RAM). This reduces latency for swapping between different applications over latency for accessing big files from a single application:
echo 15 > /proc/sys/vm/swappiness
If you prefer to keep applications nearly always in RAM you could set this to 1. If you set this to zero, kernel will not swap at all unless absolutely necessary to avoid OOM. If you were memory limited and working with big files (e.g. HD video editing), then it might make sense to set this close to 100.
I nowadays (2017) prefer to have no swap at all if you have enough RAM. Having no swap will usually lose 200-1000 MB of RAM on long running desktop machine. I'm willing to sacrifice that much to avoid worst case scenario latency (swapping application code in when RAM is full). In practice, this means that I prefer OOM Killer to swapping. If you allow/need swapping, you might want to increase /proc/sys/vm/watermark_scale_factor
, too, to avoid some latency. I would suggest values between 100 and 500. You can consider this setting as trading CPU usage for lower swap latency. The default is 10 and the maximum possible is 1000. Higher value should (according to kernel documentation) result in higher CPU usage for kswapd
processes and lower overall swapping latency.
Next, tell kernel to prefer keeping directory hierarchy in memory over file contents and the rest of the page cache in case some RAM needs to be freed (again, if everything fits in RAM, this setting does nothing):
echo 10 > /proc/sys/vm/vfs_cache_pressure
Setting vfs_cache_pressure
to a low value makes sense because in most cases, the kernel needs to know the directory structure and other
filesystem metadata before it can use file contents from the cache and flushing the directory cache too soon will make the file cache next to worthless. However, page cache contains also other data but just the file contents so this setting should be considered like the overall importance of metadata caching vs rest of the system. Consider going all the way down to 1 with this setting if you have lots of small files (my system has around 150K 10 megapixel photos and counts as a "lots of small files" system). Never set it to zero or the directory structure is always kept in memory even if the system runs out of memory. Setting this to a big value is sensible only if you have only a few big files that are constantly being re-read (again, HD video editing without enough RAM would be an example case). Official kernel documentation says that "increasing vfs_cache_pressure significantly beyond 100 may have negative performance impact".
Year 2021 update: After running with kernel version 5.4 for long enough, I've come to the conclusion that the very low vfs_cache_pressure
setting (I used to run with 1
for years) may now be causing long stalls / bad latency when memory pressure gets high enough. However, I never noticed such behavior with kernel version 5.3 or lesser.
Year 2022 update: I've been running kernel 5.4.x series for another year and I've come to the conclusion that vfs_cache_presure
has changed permanently. The kernel memory manager behavior that I used to get with kernel version 5.3 or older with values in range 1..5 seems to match real world behavior with 5.4 values in range 100..120. The newer kernels make this adjustment matter more so I'd recommend the value vfs_cache_presure=120
nowadays for low latency overall. Kernel version 5.3 or older should use a very low but non-zero value here in my opinion.
Exception: if you have a truly massive amount of files and directories and you rarely touch/read/list all files setting vfs_cache_pressure
higher than 100 may be wise. This only applies if you do not have enough RAM and cannot keep the whole directory structure in RAM and still have enough RAM for normal file cache and processes (e.g. company wide file server with lots of archival content). If you feel that you need to increase vfs_cache_pressure
above 100 you're running without enough RAM. Increasing vfs_cache_pressure
may help but the only real fix is to get more RAM. Having vfs_cache_pressure
set to high number sacrifices average performance for having a more stable performance overall (that is, you can avoid really bad worst case behavior but have to deal with worse overall performance).
Finally tell the kernel to use up to 99% of the RAM as cache for writes and instruct kernel to use up to 50% of RAM before slowing down the process that's writing (default for dirty_background_ratio
is 10
). Warning: I personally would not do this but you claimed to have enough RAM and are willing to lose the data.
echo 99 > /proc/sys/vm/dirty_ratio
echo 50 > /proc/sys/vm/dirty_background_ratio
And tell that 1h write delay is ok to even start writing stuff on the disk (again, I would not do this):
echo 360000 > /proc/sys/vm/dirty_expire_centisecs
echo 360000 > /proc/sys/vm/dirty_writeback_centisecs
For more information about these tunables, see https://www.kernel.org/doc/Documentation/sysctl/vm.txt
If you put all of those to /etc/rc.local
and include following at the end, everything will be in cache as soon as possible after boot (only do this if your filesystem really fits in the RAM):
(nice find / -type f -and -not -path '/sys/*' -and -not -path '/proc/*' -print0 2>/dev/null | nice ionice -c 3 wc -l --files0-from - > /dev/null)&
Or a bit simpler alternative which might work better (cache only /home
and /usr
, only do this if your /home
and /usr
really fit in RAM):
(nice find /home /usr -type f -print0 | nice ionice -c 3 wc -l --files0-from - > /dev/null)&
sudo mount -o ro,nobarrier /path/to/mountpoint
or adjust/etc/fstab
to includenobarrier
for any filesystem that you're willing to sacrifice for improved performance. However, if your storage device has internal battery such as Intel 320 SSD series, usingnobarrier
causes no data loss.