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

Эти два механизма часто путают, считая их двумя вариантами одного и того же. На поверхности так и есть: оба сжимают страницы, которые иначе ушли бы на диск. Но под капотом они делают принципиально разные ставки на то, как ядру управлять нехваткой памяти, и неверный выбор под конкретную ситуацию способен сделать хуже, чем вообще без подкачки. Разобраться в их различиях особенно важно владельцам систем со скромным объёмом памяти, где каждое решение о подкачке на счету.

Чем отличается сжатый блочный диск в памяти от кэша перед диском

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

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

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

Почему жёсткий предел ёмкости становится ловушкой

Самая коварная ловушка первого механизма кроется как раз в его жёстком пределе. Когда сжатое блочное устройство в памяти заполняется до отказа, у ядра почти не остаётся рычагов, чтобы исправить ситуацию. Автоматического вытеснения холодных страниц на диск у него нет, и система оказывается перед скверным выбором: либо аварийно убить процесс из-за нехватки памяти, либо свалиться на низкоприоритетную дисковую подкачку с резким провалом скорости.

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

Наглядный пример из практики отрезвляет. Если на машине с шестнадцатью гигабайтами памяти отвести половину под сжатое устройство в памяти, при хорошем сжатии оно посулит около шестнадцати гигабайт подкачки. Но под тяжёлой нагрузкой это устройство заполнится, и тогда восемь гигабайтов физической памяти окажутся заняты сжатой подкачкой, оставив рабочим задачам и кэшу всего восемь. Давление на память наступит раньше, а когда ядру придётся сбрасывать на диск, скорость рухнет. Вместо облегчения механизм усугубит нехватку.

Почему сжатый кэш перед диском справляется с давлением мягче

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

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

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

Как включить каждый из механизмов

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

sudo modprobe zram
echo lz4 | sudo tee /sys/block/zram0/comp_algorithm
echo 4G | sudo tee /sys/block/zram0/disksize
sudo mkswap /dev/zram0
sudo swapon --priority 100 /dev/zram0

Здесь загружается модуль, выбирается быстрый алгоритм сжатия, задаётся размер устройства, на нём создаётся область подкачки и включается с высоким приоритетом, чтобы система использовала её прежде дисковой. Сжатый кэш перед диском включают иначе, через параметры ядра при загрузке, и ему нужна уже существующая дисковая подкачка.

echo 1 | sudo tee /sys/module/zswap/parameters/enabled
echo lz4 | sudo tee /sys/module/zswap/parameters/compressor
echo zsmalloc | sudo tee /sys/module/zswap/parameters/zpool

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

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

Как настроить агрессивность подкачки под сжатие

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

echo 100 | sudo tee /proc/sys/vm/swappiness

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

echo "vm.swappiness=100" | sudo tee /etc/sysctl.d/99-swappiness.conf

Конкретное значение подбирают под механизм и нагрузку. Для сжатого диска в памяти умеренно высокое значение разумно, для сжатого кэша перед диском его можно оставить близким к стандартному или поднять, ведь кэш сам решит, что держать в памяти, а что сбросить вниз. Слепо выкручивать параметр на максимум не стоит, лучше проверить отзывчивость системы под реальной нагрузкой.

Как следить за эффективностью сжатия

Чтобы понять, приносит ли сжатие пользу, его работу полезно наблюдать. Для сжатого диска в памяти статистику отдаёт отдельная утилита управления этим устройством, показывающая исходный и сжатый объём данных, а значит, и реальный коэффициент сжатия.

zramctl

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

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

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

Какой механизм выбрать под свою ситуацию

Чтобы выбор не превращался в гадание, удобно опираться на простое сопоставление сценариев:

  1. Если подкачка нужна лишь изредка и укладывается в скромную долю памяти, проще и эффективнее сжатый диск в памяти;
  2. Если давление на память велико, непредсказуемо или регулярно выходит за разумные пределы, надёжнее сжатый кэш перед диском;
  3. На машине без всякого дискового накопителя или с требованием не писать данные на постоянное хранилище подходит только сжатый диск в памяти;
  4. На системе с быстрым твердотельным накопителем сжатый кэш раскрывается полнее, дёшево сбрасывая холодные страницы вниз;
  5. На очень слабом процессоре стоит выбирать самый быстрый алгоритм сжатия, иначе расходы на сжатие съедят выигрыш.

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

Что в итоге запомнить

Сжатие памяти спасает системы со скромным объёмом оперативной памяти, держа выгруженные страницы в сжатом виде прямо в памяти вместо медленного диска. Два механизма ядра решают эту задачу по-разному: один создаёт сжатый диск подкачки в памяти с жёстким пределом ёмкости, другой работает сжатым кэшем перед обычной дисковой подкачкой с автоматическим вытеснением холодных страниц.

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