Skip to content

Кеш процессора

Материал проходит ревью.

НовичокФундамент ~24 мин

Границы темы

Разбираем: почему CPU хранит копии данных рядом с ядром, что такое попадание и промах кеша и почему последовательный доступ часто быстрее случайного.

Пока не разбираем: формулы размещения строк, согласованность нескольких ядер и атаки по времени доступа.

Перед статьёй полезно знать, зачем CPU нужны регистры.

Результаты обучения

После статьи читатель сможет:

  1. расположить кеш между регистрами и RAM;
  2. объяснить попадание и промах кеша;
  3. описать смысл уровней L1, L2 и L3/LLC;
  4. привести примеры локальности;
  5. провести учебное сравнение двух порядков чтения массива.

Основная модель

CPU выполняет команды быстрее, чем RAM обычно успевает отдавать отдельные данные. Кеш уменьшает разрыв: хранит копии недавно и соседно использованных данных рядом с ядром.

Диаграмма

Чем ближе уровень к ядру, тем он обычно меньше и быстрее. Точная схема зависит от процессора: не у каждого CPU одинаковое число уровней и не все кеши разделяются между ядрами одинаково.

Основные термины

ТерминПростое определение
Попадание, cache hitНужные данные уже найдены на проверяемом уровне
Промах, cache missДанных нет, нужно идти на более далёкий уровень
Строка кешаБлок соседних байтов, переносимый как единое целое
L1Ближайший и небольшой уровень кеша
L2Следующий, обычно более крупный уровень
LLCПоследний уровень перед оперативной памятью
ЛокальностьСклонность программы использовать близкие или недавние данные

End-to-end сценарий

Пусть программа читает numbers[100].

Диаграмма

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

Что происходит под капотом

Попадание и промах

При чтении CPU сначала проверяет ближайший уровень:

Диаграмма

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

Пространственная локальность

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

text
0 → 1 → 2 → 3 → 4 → 5

обычно использует принесённые строки лучше, чем случайный:

text
5 → 10000 → 2 → 800000 → 17

Это не правило языка программирования, а свойство расположения и доступа к данным.

Временная локальность

Если значение использовано сейчас, оно может понадобиться снова в ближайшее время. Например, небольшой справочник, читаемый в каждой итерации, способен оставаться в кеше.

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

Кеш команд и кеш данных

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

Запись

При изменении данных CPU обновляет кеш, а затем система должна согласовать изменение с более дальними уровнями. Конкретная политика может записывать дальше сразу или позже.

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

Минимальный воспроизводимый пример

Эксперимент сравнивает последовательный и перемешанный обход одного массива:

js
const size = 1_000_000;
const values = new Uint32Array(size);
const order = Uint32Array.from({ length: size }, (_, index) => index);

for (let index = size - 1; index > 0; index -= 1) {
  const other = (index * 48271) % (index + 1);
  [order[index], order[other]] = [order[other], order[index]];
}

function measure(label, valueAt) {
  let sum = 0;
  const started = performance.now();
  for (let round = 0; round < 10; round += 1) {
    for (let index = 0; index < size; index += 1) {
      sum += values[valueAt(index)];
    }
  }
  console.log(label, Math.round(performance.now() - started), sum);
}

measure("Последовательно, мс:", (index) => index);
measure("Вперемешку, мс:", (index) => order[index]);

Сохраните код в cache-locality.mjs и запустите:

bash
node cache-locality.mjs

Ожидаемый результат

Обе строки заканчиваются контрольной суммой 0. На многих компьютерах перемешанный обход займёт больше времени. Точные числа и отношение не гарантируются: на них влияют CPU, Node.js, оптимизация кода и соседняя нагрузка.

Как диагностировать ошибку

  • node не найден — установите Node.js или проверьте PATH;
  • результаты сильно прыгают — повторите запуск несколько раз;
  • времена почти равны — увеличьте round или size, но следите за объёмом RAM;
  • сумма различается — варианты выполняют разную работу, сравнение некорректно;
  • один случайный запуск противоречит ожиданию — не делайте вывод по одному измерению.

Ограничения и trade-offs

Подход к даннымВозможный эффектЦена
Последовательный обходЛучше использует соседние байтыНе подходит каждому алгоритму
Маленький рабочий наборБольше повторных попаданийНужно уменьшить или разделить данные
Копирование в плотный массивУдобный доступДополнительная память и копирование
Случайные ссылки между объектамиГибкая структураВозможны частые промахи

Оптимизировать расположение данных стоит после измерения реальной программы. Более простой алгоритм часто важнее небольшой разницы в локальности.

Failure modes и типичные ошибки

  • Кеш увеличивает объём доступной RAM. Он хранит копии, а не добавляет независимое рабочее пространство приложения.
  • Попадание гарантировано после первого чтения. Строку могут вытеснить.
  • L1, L2 и L3 одинаковы на всех CPU. Размеры и совместное использование зависят от модели.
  • Последовательный доступ всегда быстрее при любом размере. Результат зависит от кода, данных и оптимизаций.
  • Нужно вручную складывать обычные переменные в L1. Аппаратный кеш управляется CPU.
  • HTTP cache miss и CPU cache miss — одно событие. Это разные уровни системы.

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

Практическое задание

Задача: выполните пример пять раз для двух размеров массива и сравните медианы.

Критерии готовности:

  • оба варианта выполняют одинаковое число чтений;
  • контрольная сумма одинакова;
  • сохранены все результаты, а не только лучший;
  • указаны размер массива и версия Node.js;
  • вывод сформулирован как наблюдение для конкретной машины.
Подсказка

Попробуйте 100_000 и 2_000_000 элементов. Не ожидайте одинакового отношения: маленький и большой рабочие наборы по-разному взаимодействуют с кешами.

Проверка знаний

ИНТЕРАКТИВНАЯ ПРОВЕРКА

Аппаратный кеш CPU

0 / 4
01Зачем CPU нужен аппаратный кеш?
02Какие обращения обычно хорошо используют кеш?
03Что происходит при чтении, если данных нет в ближайшем кеше?
  1. CPU использует значение
  2. Проверить ближайший кеш
  3. Обратиться к следующему уровню или RAM
  4. Поместить полученный блок в кеш
04Разработчик может считать, что выбранное значение всегда останется в L1.
Проверено: 0 из 4

Самопроверка

  1. Чем попадание отличается от промаха?
  2. Почему чтение соседнего элемента массива часто дешевле?
  3. Что такое временная локальность?
  4. Почему приложение не может считать значение навсегда закреплённым в L1?
Ответы и критерии
  1. При попадании данные уже есть на проверяемом уровне; при промахе нужен более далёкий уровень.
  2. В кеш переносится блок соседних байтов, поэтому элемент может уже находиться в принесённой строке.
  3. Недавно использованное значение скоро используется снова.
  4. Кеш ограничен, управляется аппаратно, а его строки вытесняются другой работой.

Краткое резюме

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

Следующие шаги

Следующая статья — «Оперативная память». Она объясняет основное рабочее хранилище, из которого кеш получает данные.

Источники