Процессоры: устройство и принципы работы

Процессоры: устройство и принципы работы Полезное

Процессор (CPU, центральный процессор) — это интегральная схема, которая исполняет машинные команды программы: читает их из памяти, выполняет арифметические и логические операции над данными и управляет остальными узлами компьютера. Физически это кристалл кремния с большим числом транзисторов, размещенный в защитном корпусе.

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

Небольшое уточнение о границах. В смартфонах и многих ноутбуках процессорные ядра входят в состав системы на кристалле (SoC) вместе с графикой и контроллерами. В настольных и серверных компьютерах процессор чаще остается отдельным компонентом. Дальше под словом «процессор» я имею в виду именно вычислительную часть.

Из чего состоит процессор

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

Ядро (core) — самостоятельный вычислительный блок, который выполняет команды. Современные процессоры многоядерные: несколько ядер работают параллельно. Внутри каждого ядра есть свои подблоки.

Устройство управления (control unit) разбирает очередную команду и раздает сигналы остальным блокам: что и в каком порядке делать.

Арифметико-логическое устройство (АЛУ, ALU) выполняет собственно вычисления — сложение, вычитание, сравнение, логические операции. Для дробных чисел работает отдельный блок с плавающей точкой (FPU).

Регистры — сверхбыстрые ячейки внутри ядра, куда кладут операнды и промежуточные результаты. Их немного и они малы по объему, но исполнительные блоки обращаются к ним напрямую, без задержки, поэтому это самая быстрая память в системе. Отдельный регистр — счетчик команд (program counter) — хранит адрес следующей инструкции.

Кэш-память нескольких уровней (L1, L2, L3) расположена прямо на кристалле и хранит то, к чему ядро обращается чаще всего. О ней подробнее ниже.

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

Как процессор выполняет команды

Программа — это последовательность машинных команд, лежащих в памяти. Процессор обрабатывает их по повторяющемуся циклу: выборка — декодирование — выполнение (fetch — decode — execute).

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

Один проход цикла для одной команды выглядит так:

  1. Выборка. По адресу из счетчика команд ядро читает очередную инструкцию из памяти (чаще всего она уже лежит в кэше, а не в медленной оперативной памяти).
  2. Декодирование. Устройство управления разбирает код операции и операнды: что делать и над чем.
  3. Выполнение. АЛУ или другой блок выполняет действие, результат попадает в регистр.
  4. Запись и переход к следующей команде. Результат при необходимости сохраняется в память, а счетчик команд получает адрес следующей инструкции. Обычно это следующая по порядку команда, но команды перехода, вызова функции и прерывания записывают в счетчик другой адрес.

Синхронизирует все это тактовый генератор. Такт — это один шаг синхронизации; тактовая частота (измеряется в гигагерцах) показывает, сколько таких шагов приходится на секунду. Важно не путать: за один такт может выполняться лишь часть команды, целая команда или, на современных ядрах, сразу несколько команд. Поэтому частота сама по себе не равна «числу команд в секунду».

Чтобы увидеть цикл целиком, ниже — минимальная модель условного процессора на Python. У него четыре регистра, счетчик команд и крохотный набор инструкций. Программа загружает два числа, складывает их и печатает результат:

program = [
    ("LOAD", 0, 2),   # R0 = 2
    ("LOAD", 1, 3),   # R1 = 3
    ("ADD", 0, 1),    # R0 = R0 + R1
    ("PRINT", 0),     # вывести R0
    ("HALT",),        # остановка
]

regs = [0, 0, 0, 0]   # регистры R0..R3
pc = 0                # счетчик команд: адрес следующей инструкции

while True:
    instr = program[pc]   # выборка команды по адресу из pc
    op = instr[0]         # декодирование: код операции
    pc += 1               # по умолчанию переходим к следующей команде
    if op == "LOAD":
        _, r, val = instr
        regs[r] = val
    elif op == "ADD":
        _, r1, r2 = instr
        regs[r1] = regs[r1] + regs[r2]
    elif op == "PRINT":
        print("R{} = {}".format(instr[1], regs[instr[1]]))
    elif op == "HALT":
        break

# Вывод: R0 = 5

Модель намеренно упрощена: в настоящем процессоре команды и данные — это числа в памяти, а не кортежи Python, и цикл реализован аппаратно. Но роли счетчика команд, регистров и АЛУ здесь ровно те же, что и в железе.

Конвейер и многоядерность

Теперь обещанное уточнение к последовательной модели. Ядро не ждет, пока одна команда пройдет все этапы, чтобы взяться за следующую. Этапы разнесены по ступеням, и они работают одновременно — это конвейер (pipeline).

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

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

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

Многоядерность — это другой уровень параллелизма. Несколько ядер выполняют разные потоки одновременно. Число ядер не всегда совпадает с числом потоков: технология одновременной многопоточности (SMT, у Intel она называется Hyper-Threading) позволяет одному физическому ядру обрабатывать два логических потока. Насколько это ускорит конкретную задачу, зависит от того, можно ли ее вообще разбить на параллельные части: рендеринг видео или компиляция большого проекта выигрывают заметно, а строго последовательный расчет — почти нет.

Кэш-память: зачем она нужна

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

Кэш делят на уровни, они отличаются объемом и скоростью:

  • L1 — самый маленький и самый быстрый, свой у каждого ядра;
  • L2 — больше по объему и немного медленнее;
  • L3 — самый большой и самый медленный из трех, обычно общий для всех ядер.

Ядро ищет нужные данные последовательно, от L1 к L3. Если их нет ни в одном уровне (промах кэша), приходится обращаться к оперативной памяти — это медленнее в разы. Чем чаще данные находятся в кэше, тем меньше таких дорогих обращений и тем быстрее работает программа.

Основные характеристики процессора

При выборе процессора в характеристиках легко утонуть. В таблице ниже — что каждая из них означает и на что реально влияет.

Характеристика Что это На что влияет и как читать
Тактовая частота (ГГц) Число тактов в секунду Скорость одиночных операций; честно сравнивать частоты можно только внутри одного поколения и архитектуры
Число ядер и потоков Сколько задач ядра тянут параллельно Производительность в параллельных задачах: рендеринг, компиляция, много приложений сразу
Разрядность (32 или 64 бита) Ширина данных и адресов, обрабатываемых за раз Объем адресуемой памяти и размер чисел за операцию; сейчас массово используется 64 бита
Техпроцесс (нм) Размер элементов на кристалле Плотность транзисторов и энергоэффективность; цифра снижается от поколения к поколению
Объем кэша (МБ) Быстрая память на кристалле Частоту дорогих обращений к оперативной памяти
TDP (Вт) Расчетная тепловая мощность под нагрузкой Требования к системе охлаждения и питанию
Сокет Тип разъема для установки на плату Совместимость с материнской платой

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

FAQ

Чем процессор отличается от видеокарты (GPU)? Процессор универсален: у него немного мощных ядер, заточенных под последовательную логику и управление всей системой. У GPU тысячи простых ядер для массово-параллельных задач — графики и машинного обучения. Для большинства повседневных задач главную роль играет именно CPU.

Что такое разгон (overclocking)? Это повышение тактовой частоты выше штатной ради дополнительной производительности. Вместе с частотой растут нагрев и энергопотребление, поэтому нужен запас по охлаждению; к тому же у многих процессоров множитель заблокирован и разгон ограничен.

Можно ли заменить только процессор, не меняя плату? Да, но лишь если новый процессор совместим с сокетом и чипсетом материнской платы и поддержан ее прошивкой. Если совместимости нет, менять придется и плату, а иногда и оперативную память.

Выводы

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

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

Освойте тему на практике

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

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)