Linux swap page-cluster readahead

archive view archive save

swap-linux-logo_1 page-cluster довольно интересный и важный параметр настройки подсистемы виртуальной памяти (VM) ядра Linux, от правильного понимания механизма работы которого зависит стабильность и производительность машины с относительно малым размером оперативной памяти и активно использующей "подкачку страниц" (paging, ошибочно swapping) с резервного хранилища (жесткого диска).

Каждый из параметров настройки ядра Linux нужно рассматривать под микроскопом и исключительно в контексте оригинальной документации, а уже после размышлять над рекомендациями из иных источников. Официальная документация значит гласит, что:

page-cluster

page-cluster controls the number of pages up to which consecutive pages are read in from swap in a single attempt. This is the swap counterpart to page cache readahead. The mentioned consecutivity is not in terms of virtual/physical addresses, but consecutive on swap space - that means they were swapped out together.

It is a logarithmic value - setting it to zero means "1 page", setting it to 1 means "2 pages", setting it to 2 means "4 pages", etc. Zero disables swap readahead completely.

The default value is three (eight pages at a time). There may be some small benefits in tuning this to a different value if your workload is swap-intensive.

Lower values mean lower latencies for initial faults, but at the same time extra faults and I/O delays for following faults if they would have been part of that consecutive pages readahead would have brought in.

Пробуем переводить на великий и могучий:

page-cluster контролирует количество страниц, которые последовательно считываются из свопа за одну попытку. Это аналог кеша страниц упреждающего чтения. Упомянутая последовательность (считывания из свопа видимо) не в виртуальных/физических адресах, а на пространстве подкачки - это означает, что они были выгружены (откуда и куда? догадайся сам) вместе. (Прим.ред.: после дефиса - "это означает" муть какая-то, не так ли? Ну, да ладно...)

Это логарифмическое значение - установка его в ноль означает «1 страница», установка в 1 означает «2 страницы», установка в 2 означает «4 страницы» и т. Д. Ноль полностью отключает упреждающее чтение подкачки.

Значение по умолчанию - три (восемь страниц за раз). Может быть несколько небольших преимуществ в настройке этого значения на другое, если ваша рабочая нагрузка интенсивно использует подкачку.

Более низкие значения означают более низкие задержки для начальных фэйлов, но в то же время дополнительные фэйлы и задержки ввода-вывода для следующих фэйлов, если бы они (кто они? фэйлы?) были частью этих последовательных страниц, которые могли бы привести к (вероятно "дополнительному") упреждающему чтению. (Прим.ред.: Нихера не понял из данного абзаца. Про какие фэйлы разговор? Про "page faults" что ли?)

Ну, вот наверное как-то так...

Ещё, на некоторых сайтах встречаются описания, где упоминается, что данный параметр управляет "количеством страниц чтения и записи из/в своп" - при этом о упреждающем (readahead) чтении не упоминают ни слова, а заявления про число страниц записи за раз оф. документация данного параметра не подтверждает.

Таким образом, приходим к такому, на мой взгляд, вполне обоснованному выводу, что:
- значение параметра page-cluster установленное выше 0 активирует упреждающее чтение (readahead) страниц подкачки из свопа обратно в РАМ (сей процесс называется swap-in, а сброс страниц из РАМ в своп swap-out).

  • page faults - ошибки страниц, это когда процесс обратился к странице РАМ, которая выгружена в своп или же не существующей ни в свопе ни в РАМе.
  • swapping-out - swapping out from ram, из РАМы в своп.
  • swapped-in - swapped in ram, из свопа в РАМу.

Чуть подробнее о page faults можно почитать по ссылке: Understanding page faults and memory swap-in/outs: when should you worry? | Scout APM Blog

Каково число страниц сбрасываемых в своп за один раз

Если параметр page-cluster не определяет число страниц сбрасываемых в своп за один раз, тогда вопрос: "Сколько же страниц за раз сбрасывается/записывается в своп и какой параметр за это отвечает?".

Вероятно, число страниц сбрасываемых в своп за один подход может изменятся в зависимости от потребностей. Однако, в главе "Chapter 11 Swap Management" документации ядра Linux написано:

11.1 Describing the Swap Area

...

Each swap area is divided up into a number of page sized slots on disk which means that each slot is 4096 bytes on the x86 for example. The first slot is always reserved as it contains information about the swap area that should not be overwritten. The first 1 KiB of the swap area is used to store a disk label for the partition that can be picked up by userspace tools. The remaining space is used for information about the swap area which is filled when the swap area is created with the system program mkswap.

11.3 Allocating a swap slot

...

Linux attempts to organise pages into clusters on disk of size SWAPFILE_CLUSTER. It allocates SWAPFILE_CLUSTER number of pages sequentially in swap keeping count of the number of sequentially allocated pages in swap_info_struct→cluster_nr and records the current offset in swap_info_struct→cluster_next. Once a sequential block has been allocated, it searches for a block of free entries of size SWAPFILE_CLUSTER. If a block large enough can be found, it will be used as another cluster sized sequence.

Область подкачки разбивается на слоты размером в страницу РАМ (4096 bytes on the x86 for example), и Linux при сбросе страниц в своп пытается записывать их кластерами размером в число страниц определённых константой SWAPFILE_CLUSTER.

Значение константы SWAPFILE_CLUSTER жёстко закодировано в исходном коде ядра swapfile.c#L199:

#define SWAPFILE_CLUSTER 256

Никаких доказательств тому, что SWAPFILE_CLUSTER как-то может быть взаимосвязан со значением page-cluster в коде ядра не обнаружено. Следовательно, оф.документация и исходный код ядра явно опровергают заявления о том, что параметр page-cluster устанавливает также и число страниц сбрасываемых в своп за один раз.

Таким образом, ещё раз приходим к вполне обоснованному выводу, что:

  • значение параметра page-cluster установленное выше 0 активирует исключительно упреждающее чтение (readahead) страниц подкачки из свопа обратно в РАМ и на объёмы записи в своп выполняемых за один раз никак не влияет.

Как определить оптимальное значение page-cluster

По умолчанию page-cluster = 3 (8 страниц за один раз).

Следует иметь ввиду, что "page-cluster" - это число страниц считываемых из свопа последовательно (consecutive). При этом, последовательность размещения страниц в своп пространстве отображает последовательность их отзыва/сброса (reclaiming) из оперативной памяти, и может не совпадать с последовательностью последующего доступа в ОЗУ!

Проблему последовательности чтения страниц из своп пространства неким патчем предлагает решить некий Huang Ying <[email protected]>, подробнее в посте mm, swap: VMA based swap readahead [LWN.net]

Установка слишком большого значения page-cluster на машинах с малым количеством оперативной памяти может существенно ухудшить производительность либо и вовсе привести к зависанию всей системы.

Думаю, что для правильного вычисления оптимального значения для page-cluster нужно полностью отключить фичу "swap readahead" установив значение page-cluster = 0 (Zero disables swap readahead completely), после чего запустить:

vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0 916416 223040  42860 546900    8   10     2    21   11   39 12  3 84  1  0
 0  0 916416 222512  42860 546900    0    0     0     0  568 2986  5  2 92  0  0
 1  0 916416 228092  42860 540868    0    0     0     0  609 1303  6  2 92  0  0
...
 0  0 386700 224688  42504 520696    0    0     0     0  905 1755 13  3 84  0  0
 0  0 386700 224712  42504 520696    0    0     0     0 1115 3419  7  3 90  0  0
 0  0 386700 228620  42504 516496    0    0     0     0  815 1500 15  3 82  0  0
 0  1 386584 227224  42540 517284  320    0  1208     0 1082 4262  9  3 80  8  0
 0  0 386576 226728  42568 517672   16    0   156    40  804 1781 15  2 80  4  0
 0  0 386576 226680  42568 517712    0    0     0     0  862 3374  7  3 90  0  0
 0  0 386576 226680  42568 517712    0    0     0     0  769 1511 10  3 87  1  0
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0 393304 237152  38576 521256    4    0     4     0 1366 5963 36  7 55  3  0
 0  0 393012 234680  38576 522764  144    0  5032     0 1664 3618 34  9 40 18  0
 0  0 393012 229876  38576 527332    0    0     0     0 1041 4041 22  7 67  5  0
...
 0  1 392384 233336  38868 523200 4244    0  4724     0 2315 7304 15 11 49 24  0
 0  0 392268 234320  38868 523708  740    0  1300     0  883 2096  8  4 76 12  0
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0 391948 234476  38868 524748  512    0  1432     0 1369 4410 21  6 67  7  0
 1  1 386748 235584  38868 527628   48    0  2844     0 1076 2019  8  4 76 12  0
 2  1 386724 233024  38868 530372   36    0  2888     0  934 3286 10  5 74 11  0
 0  0 386716 229800  38868 533780    0    0  3312     0  692 1390  6  2 74 18  0
 0  0 386696 227972  38868 535296    0    0  1440     0  619 2879  5  2 89  5  0
...здесь мы переключились на давно не использованное окно браузера...
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  1 658884 239868 137892 537556  824    0  1176     0  858 3610  8  4 49 38  0
 1  2 658568 236884 137892 539892  828    0  2076     0 1112 2494 11  6 31 52  0
 1  1 658020 235384 137892 539728  884    0   972     0 1383 4777 26  8 12 54  0
 2  2 657844 238956 137892 540472  856    0  1292     0 1463 3692 35  9 45 11  0
 5  2 657540 241248 137896 542164  644    0  2264     0 1439 4463 48  9 19 24  0
 1  5 656676 241868 138056 543968  876    0  2620    56 1052 2801 22  6  4 69  0
 2  1 656020 241496 138060 539992 1140    0  2324     0 1998 4386 57 14  6 24  0
 2  1 654676 261328 138060 540472  724    0  1140     0 4102 3937 63 11  7 19  0
 1  1 654408 265156 138060 541432  392    0  1404     0 1621 5547 54 10 26 11  0
 3  0 653424 259832 138060 542408  900    0  1044   412 1573 4305 41 14 26 19  0
 1  1 651968 262676 138060 537632 1256    0  1256     0 1831 6012 52 12 15 22  0
 2  1 651128 263552 138084 532604  676    0  1068   512 1306 3912 36 10 28 26  0
 8  0 650364 262064 138084 532576  648    0   648     0 1232 4147 48 11 26 16  0
 5  0 650196 253900 138084 543196  296    0   848     0 1604 4064 64 14 11 10  0
 0  1 649868 258564 138084 540288 1164    0  1164     0 1520 5367 34  9 20 37  0
 1  1 649812 256508 138092 540956 1688    0  2436     0 1576 4433 26  8 31 35  0

Пошевелить разные окна/процессы, "затрахать" машину до момента начала активного свопинга, и понаблюдать за значениями из колонки "swap" столбца "si" (swapped-in).

Результат вывода vmstat 1 показывает, что есть два состояния использования подкачки - это активное и пассивное. Активный swapped-in (swapped in ram, из свопа в РАМу) происходит в момент пробуждения уже запущенной, но не используемой (перешедшей в фоновый режим), программы - это когда из свопа в РАМ считываются по 5-10 тыс страниц, что составляет 10000 х 4 кб = 39,0625 МБ. Пассивный же swapped-in в среднем читает по 20-100 страниц (80-400 КБ).

Исходя же из того, что страницы читаются последовательно, то нам нужно выбрать такое значение, которое бы не было бы слишком маленьким при активном/большом swapped-in и не слишком большим при пассивном/малом.

Поэтому, на моё личное разумение, оптимальным значением будет то, которое не будет превышать максимального числа страниц или равняться половине страниц считываемых из свопа и/или сбрасываемых в своп при относительно пассивном/малом использовании подкачки. Количество "so" страниц сбрасываемых в своп также нужно брать в расчёт имхо именно в порядке их сброса из РАМ они будут последовательно читаться из своп пространства начиная с места возникнования page faults - например в своп было сброшено 200 страниц, а спустя запрошенная в РАМ page faults в свопе оказалась расположена в позиции 110 или ещё дальше, и если vm.page-cluster будет слишком большим, то получим "холостое" чтение ненужных страниц и задержки соответственно.

Так, например, если у нас обычно читается 50-100-200 страниц, и примерно столько же или чуть больше пишется в своп, а при пробуждении фоновых процессов по 3-5 тыс страниц, то в таком случае оптимальным будет последовательное чтение за один раз 64-128, что для vm.page-cluster = 6 или 7 соответственно.

  • 0 = Zero disables swap readahead completely.
  • 1 = 2
  • 2 = 4
  • 3 = 8
  • 4 = 16
  • 5 = 32
  • 6 = 64
  • 7 = 128
  • 8 = 256
  • 9 = 512
  • 10 = 1024

Внимание! Значения в столбцах "si/so" вывода vmstat по умолчанию в КБ, потому число страниц прочитанных/записанных из/в своп(а) = ?КБ / PAGESIZE (размер страницы памяти).

Важные замечания

ВАЖНО! Устанавливая значения page-cluster нужно убедиться в том, что размер считываемых за раз страниц (например vm.page-cluster будет = 7, 128 страниц по 4 кб = 512 кб) поддерживается планировщиком ввода/вывода, в том же числе не превышает и /sys/block/sd?/queue/read_ahead_kb

Размер страницы памяти и размер сектора диска в байтах можно узнать выполнив:

Только после полного отключения блокировщика скриптов и рекламы на этом месте появится полезная подсказка/ссылка/код/пример конфигурации/etc!

Не лишним будет напомнить, что полное отключение подкачки не рекомендуется потому, что она может быть даже очень полезной для систем Linux, даже если все еще есть доступная оперативная память. Ядро Linux будет перемещать страницы памяти, которые почти никогда не используются (например куски кода требуемые только при запуске/инициализации процесса), в пространство подкачки, чтобы обеспечить выделение еще большего объема кэшируемого пространства в памяти для более часто используемых страниц памяти (страница является частью памяти). Использование подкачки становится проблемой производительности, когда на ядро оказывается давление с целью непрерывного перемещения страниц памяти в оперативную память и из нее.

Раз мы так придирчиво обсуждаем page-cluster, то значит у нас оперативка в дефиците, поэтому нам не помешает обратить внимание на параметры:

Только после полного отключения блокировщика скриптов и рекламы на этом месте появится полезная подсказка/ссылка/код/пример конфигурации/etc!

Первые два параметра отвечают за размер выделенной памяти из доступной в наличии (top or free command, column avail Mem/available) под процессы записи - выполнение записи данных в файл не происходит в файл на прямую, а выполняется сначала в оперативке и уже потом выполняется сброс на диск если превышено dirty_background_ratio. Когда достигнут предел dirty_ratio, то все приложения желающие писать в кэш оперативки блокируются до тех пор, пока фоновый процесс сброса "грязных страниц" не освободит память в пределах dirty_ratio. Выбор оптимальных значений для dirty_* зависит от скорости диска и понятно, от размеров доступной в наличии оперативки. ВНИМАНИЕ! "The total available memory is not equal to total system memory." - Общая доступная память не одно и тоже, что и общий объём системной памяти!

Вторые два регулируют агресивность использования подкачки и отжима страниц из кэша РАМ. При малом размере РАМ агресивность использования свопа и отжима страниц из кэша РАМ долна быть повышенной дабы в наличии всегда был кусок свобоной оперативки под различные всплески активности свопа и прочих процессов. При этом агресивность vfs_cache_pressure отжима страниц из кэша РАМ долна быть выше агресивности swappiness - хотя и не факт. В некоторых случаях предпочтительнее сохранять кэш: vm.vfs_cache_pressure = 20

Ещё один параметр, который влияет на агресивность использования подкачки: watermark_scale_factor.

Для улучшения производительности подкачки страниц, своп лучше создавать на нескольких разделах отдельного диска от того, на котором стоит ОС, с назначением приоритета их использования:

vi /etc/fstab
...
### SWAP
/dev/sdb1 none swap defaults,pri=11 0 0
/dev/sdb2 none swap defaults,pri=11 0 0
/dev/sdb3 none swap defaults,pri=10 0 0
/dev/sdb4 none swap defaults,pri=9 0 0
/dev/sda3 none swap defaults,pri=1 0 0

Разделы подкачки используются начиная с самого высокого приоритета до самого низкого (где 32767 - самый высокий, а 0 - самый низкий). Назначение одинакового приоритета первым двум разделам приводит к записи данных на все два раздела одновременно; система не ждет, пока первый раздел подкачки заполнится, прежде чем начинает запись в следующий раздел. Система использует первые два раздела параллельно, и производительность в целом улучшается. Под своп лучше выделять разделы в начале диска, которые ближе всего к месту парковки блока головок жесткого диска.

В выше упомянутой главе "Chapter 11 Swap Management - kernel.org" сказано, что максимальное число областей (раздел и/или файл) подкачки равно 32:

11.1 Describing the Swap Area

Each active swap area, be it a file or partition, has a struct swap_info_struct describing the area. All the structs in the running system are stored in a statically declared array called swap_info which holds MAX_SWAPFILES, which is statically defined as 32, entries. This means that at most 32 swap areas can exist on a running system.

Анонимус же вместо обычных разделов подкачки будет рад использовать cryptswap (aka cryptsetup ecryptfs-utils).

Начиная с версии ядра 3.11 для более полного счастья можно активировать фичу Zswap (Wikipedia) - хранение в отведённом проценте от общей РАМы своп кэша в сжатом виде с помощью алгоритма LZO или менее продуктивного, но более быстрого, LZ4. Когда отведённый под Zswap (kernel.org) процент РАМы будет полностью заполнен, то более старые записи будут сброшены на диск в своп раздел или файл. Отведённый под Zswap процент РАМ выделяется по мере надобности, по умолчанию в одну страницу памяти Zswap впердоливается 2 страницы РАМ, а там где доступен z3fold (zsmalloc на худой конец) вместо стандартного zbud, то аж целых 3.

Чтение/запись подкачки выполняется асинхронно

Важно отметить, что чтение/запись страниц подкачки выполняется асинхронными запросами, - это значит, что при активном свопинге мы должны перенастроить планировщик ввода/вывода отдавая приоритет обработки асинхронных запросов. Планировщик CFQ по умолчанию отдаёт больший приоритет на обработку синхронных запросов.

Swap Management
https://www.kernel.org/doc/gorman/html/understand/understand014.html

11.5 Reading Pages from Backing Storage

The principal function used when reading in pages is read_swap_cache_async() which is mainly called during page faulting.

...

11.7 Reading/Writing Swap Area Blocks

...

Once it is known what blocks must be read or written, a normal block IO operation takes place with brw_page(). All IO that is performed is asynchronous so the function returns quickly. Once the IO is complete, the block layer will unlock the page and any waiting process will wake up.

Проблемы

Выше описанный способ аж никак не даст 100% точного определения оптимального значения для page-cluster, а лишь позволяет определить его приблизительно, более того у данной фичи readahead имеются некоторые баги:

$ man readahead
...
BUGS

readahead() attempts to schedule the reads in the background and return immediately. However, it may block while it reads the filesystem metadata needed to locate the requested blocks. This occurs frequently with ext[234] on large files using indirect blocks instead of extents, giving the appearance that the call blocks until the requested data has been read.

На железе с поддержкой NCQ (англ. Native Command Queuing — аппаратная установка очерёдности команд) фича readahead может препятствовать ("...read-ahead limits the advantage of a merge operation...") слиянию/объединению схожих запросов (/sys/block/sd?/queue/nomerges), в результате чего весь профит от поддержки NCQ почти теряется.

Отсюда мораль, что readahead на некоторых конфигурациях железа, как на уровне планировщика вводы/вывода, так и на уровне VM (виртуальной памяти), может кроме вреда ничего более не дать и потому подлежит полному отключению echo 0 > /sys/block/sdb/queue/read_ahead_kb и vm.page-cluster = 0 соответственно.

Бонусы

В качестве бонуса ниже выкладываю полный конфиг параметров настройки подсистемы виртуальной памяти (VM) ядра Linux из файла /etc/sysctl.conf для реальной рабочей машины активно использующей подкачку:

  • Linux example 4.9.0-8-686 #1 SMP Debian 4.9.144-3 (2019-02-02) i686 GNU/Linux;
  • 2 ГБ оперативной памяти;
  • CPU Intel(R) Pentium(R) 4 CPU 3.00GHz;
  • жёсткий диск SATA2 без поддержки NCQ (т.е. queue_depth = 1) мат.платой (т.е. не AHCI);
  • 5 ГБ шифрованной подкачки (2 раздела по 2,5 ГБ каждый) с cryptsetup + zswap;
  • в постоянной работе около 15-20 интерактивных приложений, среди которых 3 браузера по 5-10 открытых вкладок в каждом;
  • РАМ использовано 60%, около 1,2 ГБ;
  • своп используется на 40-50%, т.е. около 2 ГБ всегда в подкачке, при этом сохраняется вполне приемлемая интерактивность и отзывчивость системы в целом.

Отмечу также, что отключены патчи ядра Meldown и Spectre, используется не PAE ядро имхо с 2-мя ГБ РАМы в PAE нет никакого практического смысла, также здесь обходим стороной (это тема отдельной статьи) параметры настройки планировщиков (ака scheduler) ввода/вывода и ЦП (для ЦП есть свой планировщик), приоритеты приложений в т.ч. kswapd и прочие нюансы конфигурации всей системы, - только конфиг параметров настройки подсистемы виртуальной памяти (VM) + параметры zswap, вот он:

Только после полного отключения блокировщика скриптов и рекламы на этом месте появится полезная подсказка/ссылка/код/пример конфигурации/etc!

Для "подкачки" лучше использовать отдельный раздел, а если для подкачки используется файл, то лучше его размещать на файловой системе EXT2.


Комментарии в блоге
Новое на форуме