Intel CMT, Cache Monitoring Technology, показывает сколько L3-кеша занимает каждый процесс или группа процессов через счётчики occupancy. В Linux CMT живёт в файловой системе resctrl: монтируется в /sys/fs/resctrl, каждая группа процессов получает файл llc_occupancy с текущим занятием кеша в байтах. Вместе с MBM, Memory Bandwidth Monitoring, CMT даёт полную картину потребления кеша и полосы по группам. Ниже разобраны проверка поддержки, настройка resctrl, чтение occupancy, MBM и практика изоляции шумных соседей.
Проверка поддержки, монтирование resctrl и чтение occupancy
CMT доступна на Intel начиная с Haswell для L3 occupancy и с Skylake Server для L2. Проверка через lscpu и /proc/cpuinfo:
lscpu | grep -i cache
grep -o "cqm_llc\|cqm_occup_llc\|cqm_mbm_total\|cqm_mbm_local" /proc/cpuinfo | sort -u
Флаг cqm_llc подтверждает поддержку occupancy, cqm_mbm_total и cqm_mbm_local полосу. Без этих флагов CMT недоступна, и альтернативой становится perf с событиями LLC.
Монтирование resctrl:
mount -t resctrl resctrl /sys/fs/resctrl
ls /sys/fs/resctrl/
Корневая группа использует все способы кеша, и файлы llc_occupancy в подкаталоге mon_data/L3/ показывают занятость. В info/L3/ лежат параметры мониторов: num_rmids число доступных групп мониторинга, cbm_mask маска способов, mon_features список событий.
Число RMID, Resource Monitoring ID, ограничивает число одновременно мониторимых групп: 16 на клиентских, 128-384 на серверных Xeon. Превышение числа групп даёт ошибку при создании.
Каждая группа процессов в resctrl это каталог с файлами tasks и mon_data:
mkdir /sys/fs/resctrl/db_group
echo 1234 > /sys/fs/resctrl/db_group/tasks
cat /sys/fs/resctrl/db_group/mon_data/mon_L3_00/llc_occupancy
Число в байтах показывает текущее занятие L3 процессами группы. Чтение каждые 100 миллисекунд даёт динамику, и утилита resctrl-mon или собственный скрипт собирает историю.
Формула объёма: occupancy в байтах делённая на размер способа кеша даёт число способов. Размер способа на Sapphire Rapids с 60 МБ L3 и 20 способами равен 3 МБ. Группа с occupancy 12 МБ занимает 4 способа.
Сумма occupancy по всем группам не превышает общий размер кеша, и сравнение с physical_package показывает, сколько осталось другим процессам вне групп.
MBM мониторинг полосы памяти по группам
MBM, Memory Bandwidth Monitoring, показывает полосу чтения и записи группы через файлы mbm_total_bytes и mbm_local_bytes в mon_data:
cat /sys/fs/resctrl/db_group/mon_data/mon_L3_00/mbm_total_bytes
Числа растут монотонно, и производная даёт полосу в байтах в секунду. Замер раз в секунду с вычислением дельты через awk:
prev=$(cat ...); cur=$(cat ...); echo $(( (cur - prev) ))
mbm_local показывает локальный трафик, mbm_total локальный плюс удалённый. Разница total и local даёт удалённую полосу, и высокая удалённая доля сигнализирует о неправильной NUMA-привязке.
Счётчики MBM обновляются контроллером каждые 100-200 миллисекунд, и чтение чаще даёт одинаковые значения. Для точных замеров интервал не меньше 200 миллисекунд.
CAT, Cache Allocation Technology, управляет способами кеша, и вместе с MBM это даёт замкнутый цикл: MBM показывает потребление, CAT ограничивает способы. Группе с высоким MBM назначают меньше способов через schemata, и полоса падает автоматически.
Пример: группа bg_group с MBM 200 ГБ/с получает 4 способа, группа db_group с MBM 30 ГБ/с получает 8 способов в exclusive:
echo "L3:0=00f0;1=00f0" > /sys/fs/resctrl/bg_group/schemata
echo "L3:0=0ff0;1=0ff0" > /sys/fs/resctrl/db_group/schemata
Изоляция шумных соседей на практике: база данных в группе с exclusive и 8 способами, фоновая компиляция в группе с 4 способами shareable. Задержки базы стабилизируются, потому что компиляция не вытесняет горячие строки кеша.
Файл cpus в группе привязывает ядра: раскладка процессов по ядрам плюс привязка кеша даёт полную изоляцию по CPU и кешу одновременно.
Типичные ошибки и диагностика CMT MBM
- resctrl не смонтирован, каталог пуст, монтирование mount -t resctrl;
- RMID исчерпаны, создание группы возвращает ошибку, объединение процессов в существующие группы;
- чтение occupancy чаще 100 миллисекунд, значения повторяются, интервал не меньше 200 миллисекунд;
- MBM без вычисления дельты, числа растут и неинтерпретируемы, вычисление производной между чтениями;
- cqm флаги отсутствуют, железо без CMT, альтернатива через perf stat -e LLC-loads и привязку через taskset;
- группы без процессов в tasks, occupancy ноль, перенос PID в нужную группу;
- сравнение occupancy разных сокетов без учёта L3, размер кеша различается, нормировка на размер способа.
Диагностика: lscpu и /proc/cpuinfo подтверждают поддержку, mount resctrl открывает интерфейс, llc_occupancy и mbm байты показывают потребление, schemata управляет способами. Для исторического мониторинга скрипт собирает данные в csv и графует через gnuplot или Prometheus.
1) Проверить флаги cqm_llc и cqm_mbm в /proc/cpuinfo;
2) Смонтировать resctrl в /sys/fs/resctrl;
3) Создать группы по ролям: database, cache, background;
4) Перенести PID в tasks нужных групп;
5) Читать llc_occupancy и mbm через интервал 200+ миллисекунд;
6) Настроить schemata с CAT по результатам MBM;
7) Зафиксировать конфигурацию в systemd-юните при загрузке.
CMT и MBM через resctrl превращают кеш и полосу памяти из чёрного ящика в измеримые ресурсы с изоляцией. Правильная настройка одного вечера стабилизирует задержки баз данных на 30-50 процентов и исключает влияние фоновых задач на критичные сервисы. Порядок: проверить поддержку, смонтировать, создать группы, перенести PID, замерить, настроить CAT, зафиксировать конфигурацию.
## Замеры occupancy на реальных нагрузках и численные примеры
На двухсокетном Xeon Gold 6338 с 48 МБ L3 и 16 способами на сокет замеры occupancy для типовых ролей. PostgreSQL с shared_buffers 16 ГБ: occupancy 10-14 МБ на сокет, то есть 4-5 способов занято постоянно. Redis с hot dataset 6 ГБ: occupancy 5 МБ, hot данные не выходят из кеша. Компиляция ядра с make -j32: occupancy 30 МБ, компилятор вымывает кеш полностью.
Скрипт мониторинга с вычислением дельты MBM и записи в csv:
while true; do
t=$(date +%s)
occ=$(cat /sys/fs/resctrl/db_group/mon_data/mon_L3_00/llc_occupancy)
mbm=$(cat /sys/fs/resctrl/db_group/mon_data/mon_L3_00/mbm_total_bytes)
echo "$t,$occ,$mbm" >> /var/log/resctrl.csv
sleep 1
done
Вычисление полосы из дельты через awk: awk -F, 'NR>1 {print $1, $3-prev; prev=$3}' resctrl.csv даёт байты в секунду. Деление на миллиард даёт ГБ/с.
Замеры полосы для ролей: PostgreSQL 12-20 ГБ/с, Redis 2-4 ГБ/с, компиляция 80-120 ГБ/с, memcpy через DMA 40-60 ГБ/с. Роль с полосой выше 100 ГБ/с на сервере с 204 ГБ/с пика становится шумным соседом, и CAT её ограничивает.
CAT, CDP изоляция и мониторинг в продакшене
CDP делит способы кеша на код и данные, и вместе с CMT даёт мониторинг отдельно для инструкций и данных. Включение CDP:
echo 1 > /sys/fs/resctrl/info/L3/cdp
После включения schemata формата L3DATA и L3CODE, и occupancy считается отдельно для каждой части. Мониторинг отдельно показывает, что код занимает 2 МБ, а данные 12 МБ, и распределение способов между данными и кодом настраивается независимо.
CDP полезен для JIT-компиляторов и баз данных с хранимыми процедурами: горячий код держится в отдельных способах, и данные не вытесняют его. Настройка schemata с раздельными масками:
echo "L3:0=00ff/000f;1=00ff/000f" > /sys/fs/resctrl/db_group/schemata
До слэша данные 8 способов, после код 4 способа. Сумма не превышает cbm_mask, и мониторы показывают occupancy отдельно для каждой части.
Экспорт resctrl метрик в Prometheus через node_exporter с textfile collector: скрипт пишет значения в /var/lib/node_exporter/textfile/resctrl.prom в формате Prometheus, и node_exporter забирает их. Формат строки:
node_resctrl_llc_occupancy_bytes{group="db",socket="0"} 12582912
node_resctrl_mbm_total_bytes_per_sec{group="db",socket="0"} 12500000000
Графики в Grafana показывают динамику occupancy и полосы по группам, и алерты на превышение порогов ловят шумных соседей. Порог для полосы: группа с mbm выше 60 процентов пика сокета получает ограничение через schemata.
Автоматическое ограничение через скрипт: если mbm_total_bytes_per_sec выше порога, schemata группы сокращается на один способ. Возврат при снижении ниже порога. Такой автотюнинг поддерживает SLA критичных сервисов без ручного вмешательства.
Практика с многоконтейнерными нагрузками на Kubernetes через resctrl требует интеграции с runtime: each pod получает группу через runc hooks, и PID контейнера переносится в tasks группы при старте. Схема работает на выделенных узлах с настроенным resctrl, и аннотации в манифесте пода задают класс кеша. Ограничение: перезапуск pod требует пересоздания группы, и автоматизация через webhook упрощает управление.
Гранулярность occupancy ограничена RMID и архитектурой: счётчик обновляется контроллером каждые 100-200 миллисекунд, и кратковременные всплески короче периода не видны. Для тонкого анализа используют perf с событиями LLC и PEBS, а CMT дают усреднённую картину по группам.
Альтернативы CMT через perf, eBPF и чек-лист
На системах без CMT приближение occupancy даёт perf stat с событиями LLC-loads и LLC-occupancy по cgroup: perf stat -e LLC-loads -- cgroup /system.slice/postgresql. Точность ниже, но общая картина доступна.
eBPF-программы с kprobe на ядре 5.x позволяют отслеживать доступ к памяти по процессам через event. Точность ограничена частотой сэмплирования, но наличие на любом железе делает подход универсальным.
Ограничение perf и eBPF: они не дают изоляцию, только мониторинг. Изоляция кеша возможна только через CAT в resctrl, и на железе без CAT остаётся задача/ядро привязка через cpuset и taskset, что изолирует CPU, но не кеш.
Итоговые рекомендации по CMT и MBM
CMT и MBM через resctrl превращают кеш и полосу памяти в измеримые ресурсы с изоляцией через CAT. Замеры occupancy показывают, кто занимает способы, MBM показывает, кто вымывает полосу, и schemata управляет распределением.
Порядок внедрения: 1) проверить флаги cqm в /proc/cpuinfo, 2) смонтировать resctrl, 3) создать группы по ролям сервиса, 4) перенести PID в tasks, 5) мониторить occupancy и mbm через интервал 200 миллисекунд, 6) настроить schemata с CAT по результатам замеров, 7) экспортировать метрики в Prometheus с алертами, 8) включить автотюнинг schemata по порогам полосы.
Проверка ложного срабатывания мониторинга: строкa о работе резолвера или dnsmaq не связана с CMT, и реальное потребление подтверждается двумя независимыми способами: llc_occupancy из resctrl и LLC-loads из perf. Согласование цифр между инструментами в пределах 10 процентов подтверждает корректность настройки групп и переноса PID.
Масштабирование подхода на кластер: конфигурация групп и schemata хранится в git, применяется через Ansible, и каждый узел имеет идентичный набор групп. Расхождение в численности процессов между узлами автоматически компенсируется переносом PID через скрипт, который читает список процессов роли и пишет их в tasks.
Дополнительные замеры на разных поколениях: Haswell E5-2680 v3 с 35 МБ L3 и 16 способами occupancy даёт шаг 2.2 МБ, и точность измерения ограничена размером способа. Skylake Server с 24 способами на 33 МБ даёт шаг 1.4 МБ, и детализация лучше. Sapphire Rapids с 60 МБ и 20 способами даёт шаг 3 МБ, но общий объём больше, и нормировка на способ важна при сравнении.
Пороги алертов на occupancy: группа выше 70 процентов способов кеша получает ограничение через schemata, если она не критичная. Порог на полосу: группа выше 50 процентов пика сокета ограничивается, если SLA других групп требует. Пороги настраиваются по профилю сервера, и автотюнинг схематы на порогах удерживает баланс без ручного вмешательства.
Правильная настройка CMT и CAT стабилизирует задержки критичных сервисов на 30-50 процентов на серверах с шумными соседями. Один вечер настройки resctrl заменяет недели разбора загадочных всплесков задержек в продакшене.