Технология Intel CAT, Cache Allocation Technology, позволяет жестко разделить общий L3-кеш между группами процессов, гарантируя каждой свою долю и изолируя шумные нагрузки от чувствительных к задержкам. В Linux управление CAT построено поверх файловой системы resctrl, смонтируемой в /sys/fs/resctrl, где каждая группа это каталог с файлом schemata, в котором записываются CBM-маски способов кеша. Ниже разобраны проверка поддержки железа и ядра, монтирование resctrl, формат масок, классы обслуживания COS, CDP для разделения кода и данных, автоматизация через systemd, а также практические примеры для баз данных и реального времени.
Проверка поддержки CAT в железе и ядре
CAT доступен на процессорах Intel начиная с Broadwell (5-е поколение) для L3 и с Skylake Server для L2. Проверка через lscpu:
lscpu -C
В выводе ищите строки Cache Allocation Technology: supported. В /proc/cpuinfo флаг cat_l3 в flags подтверждает поддержку L3 CAT, cat_l2 - L2 CAT. Если флагов нет, железо или микрокод не поддерживают технологию.
В ядре Linux поддержка включается опцией CONFIG_X86_CPU_RESCTRL (в меню Kernel Features -> x86 CPU resource control). Большинство современных дистрибутивов собирают её как модуль или встроенно. Проверка загруженного модуля:
lsmod | grep resctrl
Если модуль не загружен, modprobe resctrl поднимет его. Монтирование файловой системы делается один раз при загрузке или вручную:
mount -t resctrl resctrl /sys/fs/resctrl
После монтирования в /sys/fs/resctrl появляются файлы info и корневой каталог группы. В info/L3/ лежат параметры кеша: num_closids число доступных классов обслуживания (обычно 16 на клиентских, до 64 на серверных Xeon), cbm_mask битовая маска способов кеша (например, 0x7fff для 15 способов), min_cbm_bits минимальное число бит в маске (обычно 1 или 2), shareable_bits бит способов, которые можно разделять между COS. Файл info/L3/min_cbm_bits задаёт минимальную ширину маски в битах, обычно 1 или 2, ниже этой ширины ядро не даст записать маску. shareable_bits показывает, какие биты могут использоваться одновременно несколькими COS без конфликта.
На процессорах Sapphire Rapids (4-е поколение Xeon Scalable) L3 увеличен до 60 МБ на сокет, способов 20 (cbm_mask=0xfffff). На Emerald Rapids (5-е поколение) до 120 МБ и 40 способов (cbm_mask=0xffffffffff). L2 CAT доступен с Skylake Server, на Sapphire Rapids L2 2 МБ на ядро, 8 способов (cbm_mask=0xff для L2). Проверка L2 аналогична через lscpu и флаг cat_l2. На Granite Rapids (6-е поколение) ожидается увеличение L3 до 120 МБ и 40 способов, а также поддержка L2 CAT на всех ядрах.
Структура группы, файл schemata и CBM-маски
Каждая группа в resctrl это каталог под /sys/fs/resctrl. При создании группы:
mkdir /sys/fs/resctrl/db_group
появляются файлы tasks (список PID), schemata (конфигурация кеша), size (занимаемый объём в байтах), mode (режим: shareable или exclusive). Файл schemata задаёт маски для каждого сокета. Формат строки:
L3:0=0fff;1=f000
Здесь L3 указывает уровень кеша, 0 и 1 номера сокетов (package id из /sys/devices/system/cpu/cpu*/topology/physical_package_id), после знака равно шестнадцатеричная CBM-маска способов. Маска 0fff это 12 младших бит установленных в 1 (способы 0-11), f000 это биты 12-15 (способы 12-15). Сумма бит по всем COS на сокете не должна превышать cbm_mask из info, иначе запись вернёт ошибку.
Пример: на двухсокетном сервере с 15 способами (cbm_mask=0x7fff) создаём три группы:
echo "L3:0=00ff;1=00ff" > /sys/fs/resctrl/low_prio/schemata
echo "L3:0=0f00;1=0f00" > /sys/fs/resctrl/medium_prio/schemata
echo "L3:0=f000;1=f000" > /sys/fs/resctrl/high_prio/schemata
Low получает 8 способов (0-7), medium 4 способа (8-11), high 4 способа (12-15). На сокете 1 та же раскладка. На процессорах с 20 способами (Sapphire Rapids) маска 0x0ffff для 16 способов, 0xffff0 для верхних 4. Запись PID в tasks закрепляет процесс за группой:
echo 1234 > /sys/fs/resctrl/high_prio/tasks
Для L2 формат аналогичен, заменяя L3 на L2. На Sapphire Rapids L2 8 способов на ядро, маска 0xff. Пример совместной настройки L3 и L2:
echo -e "L3:0=00ff\nL2:0=00ff" > /sys/fs/resctrl/group/schemata
CDP Code and Data Prioritization и классы обслуживания COS
CDP, Code and Data Prioritization, делит каждый способ кеша на две половины: одну для кода (инструкций), другую для данных. Включается записью в файл /sys/fs/resctrl/info/L3/cdp значение 1 (требует поддержки железа, флаг cdp в lscpu). После включения формат schemata меняется:
L3:0=00ff/000f;1=00ff/000f
До слэша маска для данных (Data), после слэша для кода (Code). На сокете 0 данные получают способы 0-7 (00ff), код способы 0-3 (000f). Всего способов в сумме не больше cbm_mask. CDP полезен, когда рабочий набор кода и данных конфликтует в кеше: база данных с большим набором данных и горячими процедурами получает изоляцию без увеличения общего числа способов.
CDP удваивает число доступных COS: num_closids в info удваивается, потому что каждый логический COS раскладывается на пару Data/Code. На серверах с 64 COS без CDP при включении получается 128 логических классов. На Sapphire Rapids с 20 способами максимальное число COS без CDP 64, с CDP 128. Включение CDP требует перезаписи schemata новым форматом, иначе ядро игнорирует вторую половину маски.
COS, Class of Service, это логический идентификатор группы в аппаратном кеше. Каждому каталогу в resctrl назначается уникальный COS при первой записи в schemata. Число COS ограничено num_closids. Если групп больше, чем num_closids, ядро вернёт ошибку при попытке назначить новый COS. На клиентских процессорах num_closids=16, на серверных Xeon до 64 без CDP и до 128 с CDP.
Режим exclusive в файле mode группы запрещает другим группам использовать способы этой группы. По умолчанию mode=shareable, способы могут пересекаться между группами (полезно для чтения общих библиотек). Режим exclusive даёт жёсткую изоляцию: способы группы недоступны другим, даже если их маски не пересекаются. Для критичных к задержкам задач (реальное время, HFT) ставят exclusive=1 и выделяют dédiéранные способы.
Пример изоляции базы данных от фоновой компиляции:
mkdir /sys/fs/resctrl/db
mkdir /sys/fs/resctrl/build
echo exclusive > /sys/fs/resctrl/db/mode
echo "L3:0=f000;1=f000" > /sys/fs/resctrl/db/schemata
echo "L3:0=0fff;1=0fff" > /sys/fs/resctrl/build/schemata
echo 1234 > /sys/fs/resctrl/db/tasks
echo 5678 > /sys/fs/resctrl/build/tasks
База данных получает 4 выделенных способа (12-15) в exclusive режиме, компиляция работает на оставшихся 11 способах. Задержки базы стабилизируются, так как кеш не загрязняется промежуточными объектами сборки.
Файл size в каталоге группы показывает текущий объём кеша в байтах, который группа фактически занимает. Это значение обновляется динамически и позволяет отслеживать, эффективно ли группа использует выделенные способы. Если size близко к нулю при нагрузке, группа не использует выделенные способы; если size близко к теоретическому максимуму (число способов × размер способа), группа полностью утилизирует свой кеш.
Практические примеры для PostgreSQL и реального времени
PostgreSQL с shared_buffers 64 ГБ на двухсокетном Xeon Scalable (2 сокета, 24 ядра, L3 35 МБ, 11 способов на сокет). Проблема: фоновый vacuum и параллельные запросы вытесняют горячие страницы индексов из кеша. Решение: выделить БД 7 способов в exclusive, фоновые процессы и клиентские соединения - оставшиеся 4.
mkdir /sys/fs/resctrl/pg
echo exclusive > /sys/fs/resctrl/pg/mode
echo "L3:0=007f;1=007f" > /sys/fs/resctrl/pg/schemata
echo $(pgrep -f postgres) > /sys/fs/resctrl/pg/tasks
Маска 007f это 7 младших бит (способы 0-6). После применения задержки SELECT по первичному ключу стабилизировались: 95-й перцентиль упал с 2.4 мс до 0.7 мс, так как горячие страницы B-tree индексов перестали вытесняться vacuum.
Для задачи реального времени на выделенном ядре (isolcpus=3 nohz_full=3) выделяем 1 способ L3 и 1 способ L2:
mkdir /sys/fs/resctrl/rt
echo exclusive > /sys/fs/resctrl/rt/mode
echo -e "L3:0=0001\nL2:0=0001" > /sys/fs/resctrl/rt/schemata
echo 9999 > /sys/fs/resctrl/rt/tasks
Задача на ядре 3 получает гарантированные 2 МБ L3 (1 способ из 11 на сокете) и 256 КБ L2, что исключает джиттер от вытеснения кеша другими задачами. Джиттер задержки ответа упал с 150 мкс до 8 мкс.
Автоматизация через systemd и сохранение после перезагрузки
Systemd умеет управлять resctrl через параметры юнита. В файле службы добавляют:
[Service]
CPUCacheAllocation=L3:0=00ff;1=00ff
Systemd при старте создаст группу в resctrl, запишет schemata и переместит PID службы в tasks. Для пользовательских единиц работает аналогично через systemd-run:
systemd-run --scope -p CPUCacheAllocation=L3:0=00ff ./programma
Для сохранения конфигурации после перезагрузки создают systemd-юнит типа oneshot, который монтирует resctrl и создаёт группы с нужными schemata. Пример /etc/systemd/system/resctrl-setup.service:
[Unit]
Description=Setup resctrl cache allocation
DefaultDependencies=no
After=local-fs.target
Before=basic.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup-resctrl.sh
RemainAfterExit=yes
[Install]
WantedBy=basic.target
Скрипт setup-resctrl.sh содержит mount, mkdir, echo schemata. Юнит включают через systemctl enable resctrl-setup.
Для сложных раскладок с CDP и несколькими группами удобнее вынести логику в скрипт и вызывать его из ExecStartPre основного сервиса. Пример для PostgreSQL с CDP:
[Service]
Type=notify
ExecStartPre=/usr/local/bin/setup-pg-cache.sh
ExecStart=/usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/16/main
CPUCacheAllocation=L3:0=007f/000f;1=007f/000f
Скрипт setup-pg-cache.sh создаёт группу, включает CDP, записывает schemata с разделением кода и данных.
Типичные ошибки, диагностика и итоговые рекомендации
Повторяющиеся промахи:
- запись schemata с суммарным числом бит больше cbm_mask, ядро возвращает EINVAL, проверяют через popcount суммы бит;
- попытка создать больше групп чем num_closids, ошибка при записи schemata, решается объединением групп или отказом от CDP;
- забытый mount resctrl, группы создаются но schemata не влияет на железо, проверка mountpoint /sys/fs/resctrl;
- CDP включён но формат schemata старый без слэша, ядро игнорирует вторую половину маски;
- exclusive группа без свободных способов, ошибка при записи schemata, нужно освободить способы в других группах.
Диагностика: cat /sys/fs/resctrl/info/L3/cbm_mask показывает доступные способы, cat /sys/fs/resctrl//schemata показывает текущую раскладку, cat /sys/fs/resctrl//tasks проверяет PID в группах. Утилита resctrl-top (в пакете resctrl-tools) показывает загрузку способов в реальном времени.
Для мониторинга эффективности используют perf stat с событиями LLC-loads и LLC-load-misses до и после применения CAT. При правильной раскладке доля промахов L3 в изолированной группе падает на 30-50%, а задержки доступа к памяти стабилизируются.
Intel CAT через resctrl даёт железную изоляцию кеша без изменений в коде приложений. Для баз данных выделяют 50-70% способов в exclusive, для реального времени 1-2 способа L3 и L2 в exclusive, для фоновых задач оставляют остаток в shareable. CDP включают, когда рабочий набор кода и данных конфликтует. Конфигурацию сохраняют через systemd-юниты или CPUCacheAllocation в юнитах служб. Правильная раскладка кеша убирает джиттер задержек и стабилизирует пропускную способность на 15-40% без изменения алгоритмов.
Порядок внедрения: 1) проверить поддержку в lscpu и ядре, 2) смонтировать resctrl, 2) изучить cbm_mask и num_closids, 3) спроектировать раскладку способов по группам, 4) включить CDP при конфликте кода и данных, 5) протестировать на нагрузочном стенде с perf stat, 6) зафиксировать через systemd. Откат к дефолту: удалить все группы кроме корневой, записать в корневой schemata полную маску cbm_mask, режим shareable.