Железо и архитектуры Железо и архитектуры: карта трека и где он начинается
0%

Железо и архитектуры: карта трека и где он начинается

Железо и архитектуры: карта трека и где он начинается

Где кончается вводный уровень и начинается этот трек

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

В портале вводный слой уже есть, и повторять его тут незачем:

Вопрос Где на него уже отвечено Что добавляет этот трек
Как из транзисторов получается «И», «ИЛИ», сумматор Булева логика и вентили что физически происходит внутри вентиля, откуда берётся задержка и почему она ограничивает частоту — глава 02
Что такое АЛУ, регистры, счётчик команд Как работает процессор как из одного такта на инструкцию получаются несколько инструкций за такт — главы 06–08
Зачем нужны кэши и почему они помогают Иерархия памяти как устроен контроллер, что такое когерентность и почему она стоит денег — главы 09 и 11
Как исходник превращается в исполнение От кода к исполнению что именно кодируется в инструкции и почему форматы такие — главы 03–05
Регистры, стек, соглашения о вызовах на практике Основы ассемблера почему ISA вообще выглядит как набор регистров, а не как что-то другое
Микроконтроллер, периферия, GPIO Аппаратные основы встраиваемых систем чем ядро микроконтроллера отличается от серверного ядра по внутреннему устройству
Кто раздаёт память, кто грузит систему, кто говорит с устройствами Управление памятью, Загрузка, Ввод-вывод и драйверы вид с другой стороны: что железо обязано предоставить, чтобы ОС смогла это сделать
Как измерить и починить медленный код Профилирование CPU, Кэш и локальность почему счётчики показывают именно это и что происходит в ядре за каждым промахом

Формулировка границы простая. Трек «производительность» учит измерять и чинить. Трек «железо и архитектуры» объясняет, почему получилось именно так, и что физически стоит за каждой цифрой в профиле. Первое можно освоить, не понимая второго, — до определённого потолка. Потолок обычно выглядит как «мы всё оптимизировали, а быстрее не становится» или «на другой машине те же изменения дали обратный эффект».

Три разных компьютера в одном корпусе

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

  1. Система команд (ISA) — контракт. Список инструкций, регистры, режимы адресации, модель памяти, механизм исключений и привилегий. Это то, что видит программа и на что рассчитывает компилятор. Контракт живёт десятилетиями.
  2. Микроархитектура — реализация контракта. Конвейер, декодеры, предсказатель переходов, окно внеочередного исполнения, кэши, векторные блоки. Меняется каждое поколение, программе невидима.
  3. Физическая реализация — конкретный кристалл: техпроцесс, площадь, тактовая частота, бюджет мощности, упаковка. Отсюда берутся деньги, тепло и сроки.

Уровни абстракции: программа, машинный код, ISA как контракт, микроархитектура, логика, транзистор и кристалл

Практическая ценность этого разделения не в терминологии. Из него следуют три рабочих вывода.

  • Одна ISA — множество совершенно разных машин. Ядро в наушниках и серверное ядро могут исполнять один и тот же двоичный код, отличаясь по площади и энергопотреблению на два-три порядка. Общего у них — только контракт.
  • Почти все «улучшения процессора» происходят ниже границы контракта. Добавить инструкцию — дорого и медленно: нужны компиляторы, библиотеки, операционные системы, обнаружение поддержки во время выполнения. Ускорить исполнение существующих инструкций — сложно, но не требует ничьего согласия.
  • Совместимость — не благотворительность, а следствие узости контракта. Чем меньше ISA обещает про то, как всё исполняется, тем больше свободы у реализации. Каждое лишнее обещание — например, точный порядок записей в память — на десятилетия связывает руки проектировщикам.

Исторический момент, когда это стало явным, — семейство IBM System/360 в 1964 году: одна архитектура команд и линейка машин разной стоимости и производительности, исполняющих один код. До этого «архитектура» и «конкретная машина» были одним и тем же понятием.

Уравнение, вокруг которого крутится весь трек

Есть ровно одно соотношение, к которому сводится любой разговор о производительности процессора:

время исполнения = количество инструкций  ×  тактов на инструкцию  ×  длительность такта
                       (IC)                        (CPI)                   (1 / частота)

Три множителя — три независимых рычага, и за каждый отвечают разные люди.

Множитель Кто на него влияет Чем платят
Количество инструкций архитектура ISA, компилятор, сам алгоритм более «плотные» инструкции сложнее декодировать; см. генерацию кода
Тактов на инструкцию микроархитектура: конвейер, суперскаляр, кэши, предсказание площадь кристалла, энергия, сложность верификации
Длительность такта техпроцесс, глубина конвейера, напряжение тепло, а при глубоком конвейере — растущая цена ошибки предсказания

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

# Модель времени исполнения и эффективного CPI.
# Сложность: O(k) по времени и O(1) по памяти, где k — число классов инструкций.
# Числа ниже — иллюстративные порядки величины для «типичного» суперскалярного
# ядра общего назначения поколений примерно 2018–2024 годов, НЕ характеристики модели.

def cpu_time(instr_count: int, cpi: float, freq_hz: float) -> float:
    """Время исполнения в секундах: IC × CPI × длительность такта."""
    return instr_count * cpi / freq_hz


def effective_cpi(base_cpi: float, miss_rate: float, miss_penalty_cycles: float,
                  mispredict_rate: float, mispredict_penalty: float) -> float:
    """CPI с учётом двух главных источников простоя: промахов памяти и ошибок предсказания.

    base_cpi для широкого ядра МЕНЬШЕ единицы: несколько инструкций за такт.
    miss_penalty_cycles — штраф похода в DRAM, порядок 200–350 тактов при ~3 ГГц.
    mispredict_penalty  — глубина восстановления конвейера, порядок 15–20 тактов.
    """
    return (base_cpi
            + miss_rate * miss_penalty_cycles
            + mispredict_rate * mispredict_penalty)


FREQ = 3.0e9          # 3 ГГц — круглое число для арифметики, не характеристика чипа
IC = 1_000_000_000    # миллиард инструкций

# Сценарий A: данные лежат плотно, переходы предсказуемы
cpi_a = effective_cpi(base_cpi=0.35, miss_rate=0.001, miss_penalty_cycles=250,
                      mispredict_rate=0.002, mispredict_penalty=17)

# Сценарий B: тот же алгоритм и то же число инструкций, но обход указателей
# по случайным адресам и переходы, зависящие от данных
cpi_b = effective_cpi(base_cpi=0.35, miss_rate=0.02, miss_penalty_cycles=250,
                      mispredict_rate=0.03, mispredict_penalty=17)

print(f"A: CPI={cpi_a:.2f}, время={cpu_time(IC, cpi_a, FREQ):.3f} с")
print(f"B: CPI={cpi_b:.2f}, время={cpu_time(IC, cpi_b, FREQ):.3f} с")
print(f"замедление в {cpi_b / cpi_a:.1f} раза при одинаковом IC")

Вывод показывает главное: при одном и том же количестве инструкций разница во времени легко достигает порядка, и берётся она не из «скорости процессора», а из того, насколько программа дружелюбна к конвейеру и к иерархии памяти. Именно поэтому подсчёт операций в алгоритме — необходимое, но недостаточное приближение; подробнее эта тема разобрана в главе подсистема памяти и в главе предсказание переходов.

Обратите внимание и на другое: третий множитель, длительность такта, в этом расчёте зафиксирован. Так было не всегда — двадцать лет назад именно он давал бесплатный ежегодный прирост. Разберём, почему он сломался, потому что из этого следует форма всей современной вычислительной техники.

Почему частота перестала расти

Динамическая мощность переключения КМОП-логики описывается соотношением, которое стоит помнить наизусть:

P_динамическая ≈ α · C · V² · f        плюс P_статическая (токи утечки)

α — доля переключающихся вентилей, C — суммарная ёмкость,
V — напряжение питания, f — частота

До середины 2000-х работало масштабирование Деннарда: при уменьшении транзисторов пропорционально снижалось и напряжение, поэтому плотность мощности оставалась примерно постоянной, а частоту можно было поднимать почти бесплатно. Затем напряжение упёрлось в физический пол: ниже определённого порога транзистор перестаёт надёжно закрываться, токи утечки растут экспоненциально, и уменьшение размеров больше не компенсирует рост потребления.

Что произошло дальше:

  • Квадрат в формуле работает в обе стороны. Поднять частоту на 20% почти всегда требует поднять напряжение; итоговый рост мощности получается кубическим по отношению к частоте. Отсюда стена: тепло невозможно снять с кристалла площадью в пару сотен квадратных миллиметров.
  • Бюджет мощности стал первичным ограничением проекта. Не «сколько транзисторов поместится», а «сколько из них можно одновременно переключать». Явление, при котором значительная часть кристалла в каждый момент вынуждена простаивать или работать на пониженной частоте, известно как тёмный кремний; термин закрепился после работы Dark Silicon and the End of Multicore Scaling, 2011.
  • Частота стала динамической величиной, а не характеристикой. Современное ядро непрерывно меняет частоту и напряжение в зависимости от температуры, числа активных ядер и типа нагрузки. Широкие векторные инструкции могут работать на пониженной частоте именно потому, что переключают больше вентилей за такт. Поэтому «частота процессора» на коробке — это верхняя граница при удачных условиях, а не режим работы.
  • Прирост производительности пришлось брать в другом месте. Осталось два источника: параллелизм (больше ядер, шире векторы, больше инструкций за такт) и специализация (блок, делающий одну задачу в десятки раз энергоэффективнее универсального ядра).

Эти два источника и определяют содержание второй половины трека: SIMD, многоядерность, ускорители и отдельная глава мощность и пределы, где разбирается вся арифметика бюджетов. Со стороны батарейных устройств та же тема с другого угла рассмотрена в главе энергия и ограничения.

Историческая линия: не романтика, а список ограничений

История нужна здесь по одной причине: каждая эпоха архитектур — это ответ на конкретное физическое ограничение, и почти все странности современных машин унаследованы оттуда.

Два конкретных числа для масштаба, оба — порядок величины, а не спецификация: у ENIAC середины 1940-х порядка семнадцати тысяч ламп и потребление порядка сотни киловатт; у первого коммерческого микропроцессора начала 1970-х порядка двух с половиной тысяч транзисторов и тактовая частота в сотни килогерц. Между ними — примерно четверть века и полное переворачивание того, что дорого, а что дёшево. Подробный разбор — в главе от ламп до кристалла.

Ключевой перевёртыш, который стоит запомнить сразу: раньше вычисление было дорогим, а память — быстрой относительно логики; сейчас наоборот. Почти вся современная микроархитектура — это машина, занятая тем, чтобы спрятать задержку памяти за полезной работой. Отсюда следуют и кэши, и внеочередное исполнение, и предзагрузка, и многопоточность на одном ядре.

Что скрывает микроархитектура: конвейер за одну диаграмму

Вводный курс описывает исполнение как последовательность «выбрать — декодировать — исполнить». Реальное ядро делает эти этапы для разных инструкций одновременно. Классическая пятиступенчатая схема выглядит так:

Читается диаграмма так: латентность одной инструкции — пять тактов, но пропускная способность в установившемся режиме — одна инструкция за такт. Это фундаментальное различие между задержкой и пропускной способностью, и оно возвращается в треке на каждом уровне: в конвейере, в кэше, в памяти, в межъядерной шине, в ускорителе.

Дальше начинается интересное, и об этом главы 06–08:

  • если инструкция 3 нуждается в результате инструкции 2, конвейер обязан либо ждать, либо перебрасывать результат напрямую (форвардинг);
  • если инструкция 2 — условный переход, то к моменту, когда его исход известен, в конвейере уже находятся следующие инструкции, выбранные по предположению;
  • широкое ядро выбирает и исполняет не одну инструкцию за такт, а несколько, и делает это не в порядке программы, сохраняя лишь видимость порядка при завершении.

Именно этот последний пункт — источник и производительности, и целого класса уязвимостей: спекулятивно исполненные инструкции отменяются архитектурно, но следы в кэше остаются. Об этом подробно в главе предсказание переходов и спекуляция.

Числа: как их приводить, чтобы не соврать

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

Правило трека: любое число сопровождается тремя оговорками — класс устройства, примерное поколение, режим измерения. Без них цифра бесполезна.

Логарифмическая шкала задержек: такт, кэши, DRAM, накопитель, сеть, диск

Так выглядит честная таблица порядков величины. Ориентир — настольные и серверные ядра x86-64 и ARM поколений примерно 2018–2024 годов при частоте порядка 3 ГГц.

Событие Порядок в тактах Порядок во времени От чего сильнее всего зависит
Попадание в L1d 4–5 около 1 нс почти не меняется между поколениями: связано с физическим размером массива
Попадание в L2 12–20 единицы нс объём L2 и топология ядра
Попадание в общий L3 40–70 и выше порядка 10–30 нс число ядер на кристалле, длина кольца или сетки, частота некэшевой части
Промах до DRAM 200–350 порядка 70–100 нс тип и частота памяти, глубина очередей контроллера, конфликты банков
Доступ к памяти чужого узла NUMA в 1,5–2 раза дольше локального топология платформы, число сокетов
Ошибка предсказания перехода 15–20 глубина конвейера конкретной микроархитектуры
Чтение с NVMe десятки–сотни мкс модель накопителя, очередь, состояние трансляции
Обмен внутри дата-центра десятые доли мс сеть и стек, а не процессор

Обратите внимание на первую строку: латентность L1 в тактах удивительно стабильна десятилетиями, потому что она определяется расстоянием, которое сигнал успевает пройти за такт. Это хорошая иллюстрация того, что архитектура — прикладная физика: скорость света и размер массива задают потолок, который не сдвинуть удачной схемотехникой.

Проверять всё это нужно на своей машине, а не по чужим таблицам:

# Что за ядро и какая иерархия кэшей — без гаданий
lscpu
lscpu --caches                       # размеры и уровни, если ядро их сообщает
getconf -a | grep -i cache           # размеры и длина строки кэша с точки зрения libc

# Топология: какие ядра логические, какие физические, как разложена память
lstopo-no-graphics --of console      # пакет hwloc
numactl --hardware                   # узлы NUMA и расстояния между ними

# Настоящий CPI и настоящие причины простоя вашей программы
perf stat -e cycles,instructions,cache-misses,branch-misses ./prog
perf stat -M TopdownL1 ./prog        # где именно теряются такты: фронт-энд, бэк-энд, спекуляция

# Реальные латентности инструкций конкретной микроархитектуры — из измерений, а не из документации
# https://uops.info/ и https://www.agner.org/optimize/

Что делать с результатом, разбирает трек производительность; почему счётчик показал именно это — главы 07–09 этого трека.

Один промах кэша целиком

Чтобы почувствовать, что стоит за строкой cache-misses в отчёте, полезно проследить один промах по шагам. Это упрощённая, но структурно верная картина для многоядерного процессора с общим кэшем последнего уровня.

Из этой картинки сразу следуют три вещи, которые невозможно вывести из вводной модели памяти. Во-первых, память отдаёт не байт, а строку кэша — поэтому соседние по адресу данные фактически бесплатны, а разбросанные стоят полной цены каждый раз. Во-вторых, промахи обрабатываются параллельно: ядро способно держать несколько незавершённых запросов, и потому десять независимых промахов стоят далеко не в десять раз дороже одного, а десять зависимых друг от друга — стоят. В-третьих, между ядрами существует протокол когерентности, и запись в строку, которую читает соседнее ядро, — это не бесплатная операция, а сообщение по внутренней сети кристалла. Отсюда растёт ложное разделение и вся тема из главы многоядерность; программная сторона того же вопроса — в статье конкурентность и параллелизм.

RISC-V: почему открытая система команд вообще имеет значение

RISC-V стоит в этом треке отдельной главой не из-за моды, а потому что это первый случай, когда контракт из первого уровня нашей стопки оказался общедоступным документом, а не собственностью компании. Разберём, что это меняет инженерно.

Что именно открыто

Открыта спецификация, а не кристаллы. Любой может реализовать RISC-V — в учебном проекте, в ПЛИС, в коммерческом чипе — без лицензионных отчислений и без разрешения. При этом конкретные реализации бывают и открытыми, и полностью проприетарными: открытость ISA не означает открытости ядра, как открытость стандарта на язык не означает открытости компиляторов.

Проект начат в 2010 году в Калифорнийском университете в Беркли как ISA для исследований и обучения — без груза совместимости и без коммерческих требований. Развитием и ратификацией спецификаций сейчас занимается некоммерческая организация RISC-V International; актуальные документы лежат на riscv.org/technical/specifications.

Базовый набор и расширения

Устройство ISA модульное, и это её главная инженерная особенность.

Обозначение Что даёт Комментарий
RV32I / RV64I базовый целочисленный набор, 32 регистра, разрядность 32 или 64 порядка сорока инструкций; регистр x0 жёстко равен нулю
RV32E вариант базового набора с 16 регистрами для крошечных встраиваемых ядер
M умножение и деление отдельно, потому что дорого по площади для мелких ядер
A атомарные операции нужны для многопоточности и ядра ОС
F / D / Q плавающая точка одинарной, двойной, четверной точности множество устройств живёт вообще без них
C сжатые 16-битные кодировки частых инструкций лечит главный недостаток фиксированной длины — плотность кода
V векторные операции с переменной длиной вектора модель отличается от SIMD с фиксированной шириной
B битовые манипуляции подмножества Zba, Zbb, Zbs
Zicsr, Zifencei доступ к управляющим регистрам, синхронизация с потоком инструкций базовая инфраструктура
G сокращение для IMAFD + Zicsr + Zifencei «универсальный» набор для машин с ОС

Отсюда типовые обозначения: RV32IMC — типичный микроконтроллер, RV64GC — типичное ядро под Linux.

Модульность решает реальную проблему: маленькому ядру не нужно тащить площадь под деление и плавающую точку, а большому — не нужно оправдываться за наследие. Но она же порождает риск фрагментации: если каждый производитель соберёт свой уникальный набор расширений, «двоичная совместимость» превратится в фикцию. Ответ на это — профили, стандартизованные наборы обязательных расширений для класса устройств (линейка RVA для процессоров приложений), чтобы дистрибутивы могли собирать один бинарник на класс машин, а не на модель. Это, пожалуй, самый показательный компромисс всей архитектуры: гибкость модульности приходится ограничивать сверху договорённостями, иначе она обесценивается.

Чем модель отличается от ARM и x86 — по существу, а не по маркетингу

Аспект x86-64 ARM (ARMv8-A и далее) RISC-V
Модель распространения ISA закрытая, реализовать вправе крайне узкий круг лицензируется: отдельно готовые ядра, отдельно право сделать своё спецификация открыта, реализация свободна
Кодирование инструкций переменная длина, 1–15 байт, тяжёлый декодер фиксированные 32 бита, есть компактные наборы фиксированные 32 бита, расширение C добавляет 16-битные
Число инструкций тысячи опкодов с учётом всех расширений и легаси-режимов сотни десятки в базе, остальное — явные расширения
Флаги условий есть регистр флагов есть нет: сравнение встроено в переход
Расширения добавляются вендором, обнаруживаются во время выполнения контролируются владельцем архитектуры ратифицируются организацией, любой может предложить
Обратная совместимость обязательство длиной в десятилетия, включая режимы 1980-х управляемая, устаревшее удаляется в новых уровнях молодая ISA, груза почти нет
Стоимость входа для производителя чипа недоступно лицензионные платежи и отчисления ноль за саму ISA, всё остальное платно как обычно

Отсутствие регистра флагов — хорошая иллюстрация того, что «открытость» тут не единственная новость. Флаги — это неявная зависимость между почти всеми инструкциями: каждая арифметическая операция пишет в общий регистр состояния, и внеочередному ядру приходится переименовывать его и распутывать эти связи. RISC-V убирает проблему в корне, ценой чуть большего числа инструкций в некоторых шаблонах кода. Ровно такой компромисс — «меньше работы аппаратуре, чуть больше инструкций» — и есть суть спора, разобранного в главе RISC и CISC.

Как это выглядит в коде — минимальная функция на RV64I:

# int sum(const int *a, long n) — сумма массива, соглашение о вызовах RISC-V:
# a0, a1 — аргументы, a0 — возвращаемое значение, t* — временные регистры
sum:
    li      a2, 0            # a2 = аккумулятор; li — псевдоинструкция, разворачивается в addi a2, x0, 0
    beqz    a1, .done        # if (n == 0) return 0
.loop:
    lw      t0, 0(a0)        # загрузка 32-битного слова: только load/store работают с памятью
    addw    a2, a2, t0       # 32-битное сложение со знаковым расширением результата
    addi    a0, a0, 4        # продвинуть указатель на sizeof(int)
    addi    a1, a1, -1       # n--
    bnez    a1, .loop        # сравнение и переход — одна инструкция, регистра флагов нет
.done:
    mv      a0, a2           # результат в a0
    ret                      # псевдоинструкция: jalr x0, 0(ra)

Два наблюдения для тех, кто читал основы ассемблера на x86: обращения к памяти отделены от арифметики (архитектура «загрузка-сохранение»), а условный переход сам выполняет сравнение. Разница в идеологии видна в четырёх строках.

Где RISC-V реально применяется сегодня, а где пока нет

Честная картина на середину 2020-х выглядит так.

Применяется и давно:

  • как встроенные управляющие ядра внутри больших чипов — контроллеры накопителей, сервисные и управляющие микроконтроллеры внутри графических процессоров и сетевых чипов, блоки безопасности. Пользователь такого ядра не видит, но по объёмам это, вероятно, основная масса поставленных ядер;
  • в микроконтроллерах и SoC для встраиваемых задач и интернета вещей — например, линейка ESP32-C построена на ядрах RISC-V;
  • в исследованиях, обучении и ПЛИС-прототипах: ISA бесплатна, симуляторы и генераторы ядер доступны, курс по архитектуре можно вести на настоящей, а не игрушечной системе команд;
  • в специализированных ускорителях, где RISC-V-ядро используется как управляющий процессор рядом с матричным или векторным блоком.

Существует, но с оговорками:

  • одноплатные компьютеры и ноутбуки-разработчики под Linux: порт riscv64 есть в основных дистрибутивах, ядро Linux, GCC, LLVM, JVM и Go поддерживают архитектуру, но зрелость экосистемы и объём предсобранных пакетов пока отстают от x86-64 и arm64;
  • серверные ядра: разработки идут, но по производительности на ядро в общедоступных изделиях паритета с лучшими коммерческими ядрами пока нет.

Пока не применяется по существу:

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

И главный вывод, который стоит сделать: открытая ISA снимает юридическое и экономическое ограничение, но не отменяет инженерную работу. Спроектировать конкурентное ядро, верифицировать его, довести до фабрики и построить экосистему компиляторов и библиотек — по-прежнему многолетний и дорогой процесс. Открытость меняет то, кто имеет право войти в эту дверь, а не то, насколько дверь тяжёлая. Разбор целиком — глава RISC-V.

Специализация: чем платят за производительность

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

Диаграмма читается как список компромиссов, а не как рейтинг. Ускоритель выигрывает только при выполнении всех условий сразу: работы много, она однотипная, данные умещаются в его памяти или подаются потоком, а стоимость переноса данных туда-обратно меньше выигрыша от счёта. Нарушьте любое — и универсальное ядро окажется быстрее, потому что не тратит время на копирование и синхронизацию. Подробно это разбирает глава ускорители; прикладная сторона — инференс и развёртывание моделей.

Карта трека

Что в каждой главе, одним предложением:

Глава О чём и зачем читать
01 От ламп до кристалла какие физические ограничения породили привычные решения и почему они пережили свои причины
02 Транзистор и вентиль что происходит внутри вентиля, откуда берутся задержка, потребление и предел частоты
03 Система команд из чего состоит контракт: форматы, адресация, модель памяти, исключения, привилегии
04 RISC и CISC в чём был спор, почему он закончился смешением и что от него осталось живого
05 RISC-V модульная открытая ISA целиком: база, расширения, профили, привилегированный режим
06 Конвейер ступени, конфликты, форвардинг, простои — механика перекрытия работы
07 Суперскалярность и OoO переименование регистров, окно инструкций, порядок завершения
08 Предсказание переходов как предсказывают, сколько стоит ошибка и почему спекуляция стала проблемой безопасности
09 Подсистема памяти ассоциативность, замещение, предзагрузка, когерентность, устройство контроллера
10 SIMD и векторы фиксированная ширина против переменной длины вектора, автовекторизация и её пределы
11 Многоядерность модели памяти, барьеры, атомарные операции, цена согласованности
12 Ускорители GPU, NPU, FPGA, ASIC: чем отличаются модели исполнения и когда оправданы
13 Мощность и пределы бюджет ватт, тёмный кремний, динамическое управление частотой, охлаждение
14 Производство литография, что на самом деле означают названия техпроцессов, выход годных, чиплеты
15 Выбор железа под задачу как читать характеристики и как ставить эксперимент вместо чтения рекламы

Три маршрута чтения

  • «Понять свой профиль» — для тех, кто оптимизирует код: 06 → 07 → 08 → 09 → 10 → 15. Цель — переводить показания счётчиков в понимание того, где именно ядро простаивает.
  • «Понять, почему всё так устроено» — для широкой картины: 01 → 02 → 03 → 04 → 05 → 13 → 14. Цель — увидеть цепочку от физики к контракту и обратно; программистский опыт почти не нужен.
  • «Выбрать железо» — для инфраструктурных и продуктовых решений: 13 → 12 → 09 → 11 → 15. Цель — понимать, за что вы платите деньгами, ваттами и сложностью, и как проверить обещания измерением.
  • «Мелкое ядро под задачу» — для встраиваемых разработчиков: 02 → 03 → 05 → 06 → 13, параллельно с треком встраиваемые системы. Цель — считать такты и микроамперы на одном и том же кристалле.

Типичные заблуждения, которые трек снимает

  • «Больше гигагерц — быстрее». Частота — один из трёх множителей, и обычно наименее подвижный. Ядро с меньшей частотой и большей шириной исполнения регулярно оказывается быстрее на реальном коде.
  • «Ядер больше — значит быстрее». Прирост ограничен долей параллельной части работы и стоимостью согласования; при интенсивной записи в общие данные добавление ядер иногда замедляет систему.
  • «Кэш просто ускоряет память». Кэш меняет стоимость доступа в зависимости от шаблона обращения. Программа с плохой локальностью не получает от кэша почти ничего, сколько бы его ни было.
  • «Компилятор всё оптимизирует». Компилятор не может изменить раскладку ваших данных в памяти и не знает вероятностей ваших ветвлений. Он оптимизирует то, что вы ему дали.
  • «Число в названии техпроцесса — это размер транзистора». Уже давно нет: это название поколения, а не измеренная величина. Разбор — в главе 14.
  • «Ускоритель всегда быстрее». Ускоритель быстрее при большом объёме однотипной работы и дешёвой передаче данных. На мелких разрозненных задачах он проигрывает из-за накладных расходов.
  • «RISC-V победит, потому что бесплатен». Бесплатна спецификация. Конкурентоспособность даёт реализация и экосистема, а они стоят так же дорого, как и раньше.

Чек-лист: трек усвоен, если вы можете

  1. Объяснить разницу между ISA и микроархитектурой на конкретном примере и сказать, что из наблюдаемого поведения относится к какому уровню.
  2. Разложить время работы программы на три множителя и назвать, какой из них вы реально можете двигать в своей задаче.
  3. Прочитать вывод perf stat и сказать, где ядро простаивает: на фронт-энде, на памяти или на неверной спекуляции.
  4. Объяснить, почему частота перестала расти, через формулу мощности, а не через фразу «упёрлись в физику».
  5. Рассказать, что даёт модульность RISC-V и какой ценой, не используя слова «современный» и «инновационный».
  6. Оценить, стоит ли задача переноса на ускоритель, до того как её туда переносить.
  7. Привести число латентности и самому же назвать три оговорки к нему.

Источники

Книги, к которым трек отсылает постоянно:

  • Patterson D., Hennessy J. Computer Organization and Design, RISC-V Edition — вводит архитектуру именно через RISC-V, лучший стартовый текст.
  • Hennessy J., Patterson D. Computer Architecture: A Quantitative Approach — базовый труд по количественному подходу: конвейеры, память, параллелизм, ускорители.
  • Hennessy J., Patterson D. A New Golden Age for Computer Architecture, Тьюринговская лекция: cacm.acm.org — аргумент о том, почему специализация вернула архитектуру в центр разговора.
  • Drepper U. What Every Programmer Should Know About Memory: PDF — устарел в числах, но не в механике.
  • Esmaeilzadeh H. и др. Dark Silicon and the End of Multicore Scaling: doi.org/10.1145/2000064.2000108.

Первоисточники и измерения:

Что дальше

Начинаем с фундамента: От ламп до кристалла: как физика формировала архитектуру — почему каждое поколение элементной базы диктовало свой стиль архитектуры, какие решения были вынужденными, а какие пережили породившие их ограничения и остались с нами до сих пор.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов