中文翻译暂不可用,显示俄语原文。

Как устроена иерархия памяти GPU (Global, L2, Shared, Registers) и как это влияет на LLM инференс?

Краткий тезис

Иерархия памяти GPU — это многоуровневая система с разной скоростью и объёмом: Global Memory (HBM) — медленная, но большая (~80 ГБ на H100, ~2 ТБ/с), L2 Cache — промежуточный буфер, Shared Memory — быстрая ручная память внутри SM (~20 ТБ/с, до 228 КБ на SM), Registers — самые быстрые (регистры на поток). Для LLM инференса узким местом является memory bandwidth, а не compute, потому что веса и активации пересылаются из HBM в вычислительные блоки. Понимание иерархии позволяет применять техники вроде Flash Attention, kernel fusion и quantization, которые минимизируют обращения к медленной памяти и ускоряют инференс.


1. Зачем нужна иерархия памяти в GPU

GPU (Graphics Processing Unit) выполняет тысячи потоков параллельно. Чтобы питать вычислительные ядра данными, требуется огромная пропускная способность. Однако физически невозможно сделать всю память одновременно быстрой и большой — компромисс решается иерархией:

  • Ближе к ядрам — меньше задержка, выше пропускная способность, но малый объём.
  • Дальше от ядер — больше объём, но медленнее.

Для LLM инференса (вывод нейросети) характерны операции матричного умножения (GEMM) и attention, которые требуют многократного чтения весов и промежуточных активаций. Если данные лежат в медленной памяти, ядра простаивают в ожидании — это и есть bottleneck memory bandwidth.


2. Уровни иерархии памяти GPU (на примере NVIDIA H100)

2.1 Global Memory (HBM — High Bandwidth Memory)

  • Расположение: вне чипа, на подложке.
  • Объём: 80 ГБ (H100 SXM), до 144 ГБ (GH200).
  • Пропускная способность: ~2 ТБ/с (H100), ~3.35 ТБ/с (H200).
  • Латентность: ~200–400 тактов (несколько сотен наносекунд).
  • Доступ: все потоки могут читать/писать, но медленно.
  • Роль: хранит веса модели, входные данные, KV-cache, промежуточные результаты.

Термин: HBM — стек из DRAM-чипов, соединённых через interposer с GPU. Обеспечивает высокую пропускную способность за счёт широкой шины (1024 бит и более).

2.2 L2 Cache

  • Расположение: на кристалле, общий для всех SM (Streaming Multiprocessors).
  • Объём: 50–60 МБ (H100).
  • Пропускная способность: ~4–5 ТБ/с (оценка).
  • Латентность: ~50–100 тактов.
  • Доступ: автоматический кеш для глобальной памяти.
  • Роль: уменьшает количество обращений к HBM, кешируя часто используемые данные (например, веса при batch-инференсе).

2.3 Shared Memory

  • Расположение: внутри каждого SM, распределяется между блоками потоков.
  • Объём: до 228 КБ на SM (H100, конфигурируется).
  • Пропускная способность: ~20 ТБ/с (на SM, суммарно по всем SM ~80 ТБ/с).
  • Латентность: ~5–10 тактов.
  • Доступ: ручное управление (программист явно копирует данные из глобальной памяти).
  • Роль: используется для tiling — загрузки подматриц весов/активаций и повторного использования. Ключевая для Flash Attention.

Термин: SM — вычислительный блок GPU, содержит ядра CUDA, warp-планировщик, shared memory, регистры.

2.4 Registers

  • Расположение: внутри каждого ядра CUDA (потока).
  • Объём: 255 регистров на поток (H100, 32-битных), всего ~256 КБ на SM (распределяется между потоками).
  • Пропускная способность: ~100 ТБ/с (на SM, оценка).
  • Латентность: 0 тактов (доступ за один такт).
  • Доступ: только текущий поток.
  • Роль: хранение локальных переменных, промежуточных результатов вычислений.

3. Сравнительная таблица уровней памяти (H100 SXM)

УровеньТипОбъём (на весь GPU)Пропускная способность (приблизительно)Латентность (такты)Управление
Global (HBM)DRAM80 ГБ2 ТБ/с200–400Автоматическое (load/store)
L2 CacheSRAM50–60 МБ4–5 ТБ/с50–100Автоматическое (кеш)
Shared MemorySRAM228 КБ на SM (всего ~30 МБ)~20 ТБ/с на SM5–10Ручное (__syncthreads, copy)
RegistersSRAM255 рег./поток (всего ~256 КБ на SM)~100 ТБ/с на SM0Компилятор / программист

Ключевой вывод: пропускная способность растёт на 1–2 порядка при движении вниз по иерархии, но объём падает. LLM инференс упирается в Global Memory — именно её bandwidth лимитирует скорость.


4. Как иерархия влияет на LLM инференс

4.1 Bottleneckmemory bandwidth, не compute

LLM — это memory-bound задача. Рассмотрим матричное умножение Y = X * W (например, в линейном слое):

  • X (активации) и W (веса) лежат в HBM.
  • Для каждого элемента результата нужно прочитать целые строки/столбцы из HBM.
  • Вычислительные ядра (Tensor Cores) могут выполнять умножение за несколько тактов, но данные подаются медленно.

Формула:

Время выполнения ≈ (объём данных) / (bandwidth HBM) + (вычислительное время)

Для LLM первое слагаемое доминирует.

Пример: слой с весами 4 ГБ (FP16), batch size = 1, bandwidth = 2 ТБ/с → минимальное время чтения = 4 ГБ / 2 ТБ/с = 2 мс. Реальное время больше из-за накладных расходов.

4.2 Влияние на attention

Attention (softmax(QK^T) V) требует чтения KV-cache из HBM для каждого токена. KV-cache растёт с длиной последовательности и batch size. Это делает long-context инференс особенно чувствительным к bandwidth.

4.3 Роль shared memory и L2

  • Shared memory позволяет загрузить блок весов/активаций и переиспользовать его несколько раз (tiling). Это уменьшает количество обращений к HBM.
  • L2 cache автоматически кеширует веса, если они используются повторно (например, при batch inference несколько запросов используют одни и те же веса). Но при batch size = 1 кеш малоэффективен.

5. Техники оптимизации, основанные на иерархии

5.1 Kernel Fusion (слияние ядер)

Объединение нескольких последовательных операций (например, layer norm + linear + activation) в одно ядро. Промежуточные результаты остаются в shared memory/registers, не записываются в HBM. Снижает число обращений к глобальной памяти.

5.2 Flash Attention

Алгоритм, который разбивает attention на блоки, помещающиеся в shared memory. Вычисляет softmax по частям, не сохраняя полную матрицу QK^T в HBM. Пропускная способность shared memory (~20 ТБ/с) используется вместо HBM (~2 ТБ/с). Результат: ускорение в 2–4 раза и линейная сложность по памяти.

5.3 Quantization (квантизация)

Снижение точности весов (FP16INT8/FP4) уменьшает объём данных, читаемых из HBM. Например, INT8 даёт 2x меньше данных → 2x ускорение (при фиксированной bandwidth). Это прямое воздействие на bottleneck.

5.4 Tiling (разбиение на блоки)

Ручное копирование подматриц из HBM в shared memory, вычисление, запись результата. Используется в оптимизированных библиотеках (cuBLAS, CUTLASS).


6. Пример: профилирование memory-bound операции в PyTorch

import torch
import time

# Параметры: матрица весов 4 ГБ (FP16)
N = 16384
M = 16384
dtype = torch.float16
W = torch.randn(N, M, dtype=dtype, device='cuda')
X = torch.randn(1, N, dtype=dtype, device='cuda')

# Прогрев
for _ in range(10):
    Y = X @ W
torch.cuda.synchronize()

# Замер
start = time.time()
for _ in range(100):
    Y = X @ W
torch.cuda.synchronize()
end = time.time()

avg_time = (end - start) / 100
print(f"Среднее время: {avg_time*1000:.2f} мс")
# Ожидаем ~2-3 мс (ограничение bandwidth HBM)

Результат: время близко к теоретическому минимуму (объём данных / bandwidth). Если бы compute был bottleneck, время было бы меньше.


7. Пет-проект для закрепления

Задача: Написать микро-бенчмарк, измеряющий пропускную способность разных уровней памяти GPU (Global, Shared, Registers) и показать, что LLM инференс ограничен Global Memory.

Инструменты: Python + PyTorch (или CUDA C), torch.cuda.Event для точного замера времени.

Шаги:

  1. Global Memory benchmark: загрузить большой тензор (например, 1 ГБ) и выполнить поэлементное сложение (bandwidth-bound). Измерить пропускную способность.
  2. Shared Memory benchmark: написать CUDA-ядро, которое копирует блок данных из глобальной в shared memory, выполняет операции и записывает обратно. Измерить скорость.
  3. Register benchmark: выполнить операции с локальными переменными (без обращений к памяти).
  4. Сравнить полученные значения с теоретическими (2 ТБ/с, 20 ТБ/с, 100 ТБ/с).
  5. Связать с LLM: оценить, сколько времени занимает чтение весов модели (например, Llama-7B ~14 ГБ FP16) и сравнить с реальным временем инференса.

Ожидаемый результат: Таблица с измеренными bandwidth, подтверждающая, что Global Memory — узкое место. Вывод: для ускорения инференса нужно уменьшать объём данных (квантизация) или эффективнее использовать shared memory (Flash Attention).


8. Связь с другими вопросами

ВопросТема
302Как работает Flash Attention и почему он ускоряет инференс?
303Какие методы квантизации (GPTQ, AWQ, bitsandbytes) вы знаете и как они влияют на latency?
304Как вы оптимизируете KV-cache для long-context?
305Что такое kernel fusion и как его применить к LLM?
306Как вы профилируете производительность инференса LLM?
307Как устроен Tensor Core и как он используется в LLM?

9. Навигация


Навигация