📌 Cache

Cache — сверхбыстрая энергозависимая память, предназначенная для временного хранения данных и инструкций, к которым CPU или GPU обращаются чаще всего. Используется для компенсации разницы скоростей между процессором и основной памятью (RAM), минимизации задержек при доступе к данным, повышения пропускной способности вычислительной системы.


🧠 Как работает

Архитектура и уровни

Cache обычно многоуровневая:

  • L1 (Level 1): самый быстрый и маленький (типично 16–128 КБ на ядро), разделён на data/instruction (D/I-кэш), расположен максимально близко к CPU.
  • L2 (Level 2): больше (256 КБ – 2 МБ на ядро или общий для кластера), чуть медленнее L1.
  • L3 (Level 3): общий для нескольких ядер, объём до десятков мегабайт, заметно медленнее L2, соединён через отдельную Bus или Ring bus.

Cache реализуется на SRAM, что обеспечивает доступ в несколько наносекунд (против десятков-сотен для DRAM).

Принцип работы

  • Принцип локальности: данные/инструкции, которые недавно использовались или физически близки к используемым, скорее всего понадобятся снова.
  • Кэширование блоков (lines): данные копируются из RAM целыми строками (обычно 32–128 байт).
  • Маппинг: прямая (direct-mapped), полностью ассоциативная (fully associative), сета-ассоциативная (set-associative) схемы.
  • Стратегии замещения: LRU, FIFO, Random, PLRU и др.
  • Управление когерентностью: для многоядерных CPU реализуется MESI/MOESI протоколы.

Последовательность обращения:

  1. CPU делает запрос к адресу данных.
  2. Cache Controller проверяет, есть ли строка с нужным адресом (“hit”).
    • Если есть (cache hit): данные мгновенно передаются CPU.
    • Если нет (cache miss): данные загружаются из RAM, возможна замена одной из существующих строк.
  3. Запись реализуется как write-through или write-back (с отложенной записью в RAM).
flowchart TD
    CPU[[CPU]] -->|Address| L1[L1 Cache]
    L1 -->|Miss| L2[L2 Cache]
    L2 -->|Miss| L3[L3 Cache]
    L3 -->|Miss| RAM[RAM]
    L1 -->|Hit| CPU
    L2 -->|Hit| L1
    L3 -->|Hit| L2

⚙️ Где применяется

  • Внутри процессоров (CPU, GPU, DSP), микроконтроллеров (MCU), FPGA.

  • Серверы, рабочие станции, встраиваемые и мобильные платформы.

  • Специализированные ускорители (AI accelerator, NIC).

  • Кэш-директории и snoop-фильтры в многопроцессорных и NUMA-системах.


✅ Преимущества

  • Минимизация задержек при доступе CPU к данным и инструкциям.

  • Значительное повышение производительности без роста частоты/энергопотребления.

  • Уменьшение нагрузки на RAM и Memory Bus.

  • Масштабируемость: возможность увеличивать объём/уровни кэша для разных классов устройств.

  • Повышение энергоэффективности вычислений.


❌ Недостатки

  • Заметная стоимость производства из-за использования SRAM (дороже DRAM).

  • Осложнение архитектуры: многоканальность, когерентность, кэш-линии, блокировки.

  • Сложные сценарии гонок/когерентности в многоядерных системах (snoop, false sharing).

  • Уязвимость к ряду атак по сторонним каналам (Spectre, Meltdown, Flush+Reload).

  • Ограниченный объём (физически невыгодно делать большие кэши).

  • Влияет на критический путь микросхемы (добавляет задержки между этапами pipeline).


🔗 Связанные технологии

SRAM, DRAM, CPU, GPU, MCU, L1, L2, L3, MESI, MOESI, NUMA, Memory Bus, Pipeline, RAM, Write-back, Write-through, AI accelerator


Резюме

Cache — ключевой компонент, позволяющий резко повысить производительность вычислительных систем за счёт минимизации времени доступа CPU/GPU к часто используемым данным и инструкциям. Многоуровневая структура (L1, L2, L3), использование быстрого SRAM, протоколы когерентности и оптимизация под реальные паттерны обращений позволяют кэшу быть эффективным буфером между медленной RAM и высокоскоростным ядром процессора. Главные минусы: стоимость, сложность поддержки когерентности, ограничение по объёму и атаки по сторонним каналам.


Примеры кода

C: Моделирование кэш-промахов

#include <stdio.h>
#define SIZE 4096*1024
 
int arr[SIZE];
 
int main() {
    long long sum = 0;
    for (int stride = 1; stride <= 1024; stride *= 2) {
        for (int i = 0; i < SIZE; i += stride)
            sum += arr[i];
        printf("Stride: %d, Sum: %lld\n", stride, sum);
    }
    return 0;
}

Данный код иллюстрирует зависимость промахов кэша от размера stride.

Ассемблер: чтение данных с проверкой попадания в L1

section .bss
    buffer resb 64
 
section .text
    global _start
_start:
    mov eax, [buffer]    ; Чтение кэш-линии из памяти (возможен L1 hit)
    add eax, 1
    mov [buffer], eax    ; Запись в ту же кэш-линию
    mov eax, 1
    int 0x80

C: определение размеров кэша CPU в Linux

#include <stdio.h>
#include <stdlib.h>
 
int main() {
    system("lscpu | grep 'cache'");
    return 0;
}

Источники: Intel® SDM, ARM Architecture Reference Manual, osdev.org, Википедия, habr.com, спецификации JEDEC, публикации ACM/IEEE по архитектуре процессоров.