Мы все думаем о ЦП как о «мозгах» компьютера, но что это на самом деле означает? Что происходит внутри с миллиардами транзисторов, которые заставляют работать ваш компьютер? В этой мини-серии, состоящей из четырех частей, мы сосредоточимся на проектировании аппаратного обеспечения компьютера и рассмотрим все тонкости того, что заставляет компьютер работать.
Серия будет посвящена компьютерной архитектуре, проектированию схемы процессора, СБИС (очень крупномасштабная интеграция), производству микросхем и будущим тенденциям в области вычислительной техники. Если вас всегда интересовали детали внутренней работы процессоров, оставайтесь с нами, потому что это то, что вам нужно знать, чтобы начать работу.
Часть 1: Основы компьютерной архитектуры
(архитектура набора инструкций, кэширование, конвейеры, гиперпоточность)
Часть 2: Процесс проектирования ЦП
(схемы, транзисторы, логические элементы, тактирование)
Часть 3: Схема и физическое построение чипа
(СБИС и изготовление кремния)
Часть 4: Текущие тенденции и будущие горячие темы в компьютерной архитектуре (Sea of Accelerators, 3D-интеграция, FPGA, вычисления в ближней памяти)
Мы начнем с очень высокого уровня того, что делает процессор и как строительные блоки объединяются в функционирующую конструкцию. Сюда входят ядра процессора, иерархия памяти, прогнозирование ветвлений и многое другое. Во-первых, нам нужно базовое определение того, что делает ЦП. Самое простое объяснение состоит в том, что ЦП следует набору инструкций для выполнения некоторой операции с набором входных данных. Например, это может быть чтение значения из памяти, затем добавление его к другому значению и, наконец, сохранение результата обратно в память в другом месте. Это также может быть что-то более сложное, например, деление двух чисел, если результат предыдущего вычисления больше нуля.
Когда вы хотите запустить программу, например операционную систему или игру, сама программа представляет собой набор инструкций для выполнения процессором. Эти инструкции загружаются из памяти и на простом процессоре выполняются одна за другой до завершения программы. Хотя разработчики программного обеспечения пишут свои программы на языках высокого уровня, таких как, например, C++ или Python, процессор этого не понимает. Он понимает только 1 и 0, поэтому нам нужен способ представления кода в этом формате.

Программы компилируются в набор низкоуровневых инструкций, называемых языком ассемблера, как часть архитектуры набора инструкций (ISA). Это набор инструкций, которые ЦП должен понимать и выполнять. Некоторые из наиболее распространенных ISA - это x86, MIPS, ARM, RISC-V и PowerPC. Точно так же, как синтаксис для написания функции в C++ отличается от функции, которая делает то же самое в Python, каждая ISA имеет свой синтаксис.
Эти ISA можно разделить на две основные категории: фиксированной длины и переменной длины. RISC-V ISA использует инструкции фиксированной длины, что означает, что определенное предопределенное количество битов в каждой инструкции определяет ее тип. Это отличается от x86, в котором используются инструкции переменной длины. В x86 инструкции могут быть закодированы по-разному и с разным количеством битов для разных частей. Из-за этой сложности декодер инструкций в процессорах x86 обычно является самой сложной частью всей конструкции.
Инструкции фиксированной длины упрощают декодирование благодаря их регулярной структуре, но ограничивают общее количество инструкций, которые может поддерживать ISA. В то время как распространенные версии архитектуры RISC-V содержат около 100 инструкций и имеют открытый исходный код, x86 является частной собственностью, и никто точно не знает, сколько существует инструкций. Обычно люди считают, что существует несколько тысяч инструкций для x86, но точное число не разглашается. Несмотря на различия между ISA, все они выполняют по существу одну и ту же основную функциональность.

Пример некоторых инструкций RISC-V. Код операции справа является 7-битным и определяет тип инструкции. Каждая инструкция также содержит биты, определяющие, какие регистры использовать и какие функции выполнять. Вот как инструкции по сборке разбиваются на двоичные файлы для понимания процессором.
Теперь мы готовы включить наш компьютер и начать работу. Выполнение инструкции на самом деле состоит из нескольких основных частей, которые разбиты на множество этапов процессора.
Первый шаг - загрузить инструкцию из памяти в ЦП, чтобы начать выполнение. На втором этапе инструкция декодируется, чтобы ЦП мог определить тип инструкции. Существует много типов, включая арифметические инструкции, инструкции ветвления и инструкции памяти. Как только ЦП узнает, какой тип инструкции он выполняет, операнды для инструкции собираются из памяти или внутренних регистров ЦП. Если вы хотите добавить число A к числу B, вы не сможете выполнить сложение, пока не узнаете значения A и B. Большинство современных процессоров являются 64-битными, что означает, что размер каждого значения данных составляет 64 бита.

64-бит относится к ширине регистра ЦП, пути данных и/или адресу памяти. Для обычных пользователей это означает, сколько информации компьютер может обрабатывать за раз, и это лучше всего понять на фоне его меньшего архитектурного родственника, 32-разрядного. 64-разрядная архитектура может одновременно обрабатывать в два раза больше информации (64 бита вместо 32).
После того, как процессор получает операнды для инструкции, он переходит к этапу выполнения, где операция выполняется на входе. Это может быть добавление чисел, выполнение логических манипуляций с числами или просто передача чисел без их изменения. После вычисления результата может потребоваться доступ к памяти для сохранения результата, или ЦП может просто сохранить значение в одном из своих внутренних регистров. После сохранения результата ЦП обновит состояние различных элементов и перейдет к следующей инструкции.
Это описание, конечно, является огромным упрощением, и большинство современных процессоров разбивают эти несколько стадий на 20 или более меньших стадий для повышения эффективности. Это означает, что хотя процессор будет запускать и заканчивать несколько инструкций в каждом цикле, для выполнения любой одной инструкции от начала до конца может потребоваться 20 или более циклов. Эта модель обычно называется трубопроводом, так как требуется некоторое время, чтобы заполнить трубопровод и чтобы жидкость полностью прошла через него, но как только он заполнится, вы получите постоянный выход.

Пример 4-ступенчатого конвейера. Цветные прямоугольники представляют собой инструкции, независимые друг от друга. Изображение предоставлено Википедией
Весь цикл, через который проходит инструкция, представляет собой очень четко срежиссированный процесс, но не все инструкции могут завершиться одновременно. Например, сложение выполняется очень быстро, тогда как деление или загрузка из памяти могут занимать сотни циклов. Вместо того, чтобы останавливать весь процессор, пока выполняется одна медленная инструкция, большинство современных процессоров выполняются не по порядку. Это означает, что они будут определять, какую инструкцию будет наиболее полезно выполнять в данный момент времени, и буферизовать другие инструкции, которые не готовы. Если текущая инструкция еще не готова, процессор может перейти вперед по коду, чтобы увидеть, готово ли что-нибудь еще.
В дополнение к неупорядоченному выполнению типичные современные процессоры используют так называемую суперскалярную архитектуру. Это означает, что в любой момент времени процессор одновременно выполняет множество инструкций на каждом этапе конвейера. Он также может ждать еще сотен, чтобы начать свою казнь. Чтобы иметь возможность выполнять много инструкций одновременно, процессоры будут иметь несколько копий каждой стадии конвейера внутри. Если процессор видит, что две инструкции готовы к выполнению и между ними нет зависимости, то вместо того, чтобы ждать их завершения по отдельности, он выполнит их обе одновременно. Одна из распространенных реализаций этого называется одновременной многопоточностью (SMT), также известной как Hyper-Threading. Процессоры Intel и AMD в настоящее время поддерживают двустороннюю SMT, а IBM разработала микросхемы, поддерживающие до восьми сторон SMT.

Для выполнения этого тщательно срежиссированного исполнения процессор имеет много дополнительных элементов в дополнение к основному ядру. В процессоре есть сотни отдельных модулей, каждый из которых служит определенной цели, но мы рассмотрим только основы. Двумя самыми большими и наиболее полезными являются кэши и предсказатель ветвлений. Дополнительные структуры, которые мы не будем рассматривать, включают такие вещи, как буферы переупорядочивания, таблицы псевдонимов регистров и станции резервирования.
Назначение кешей часто может сбивать с толку, поскольку они хранят данные точно так же, как ОЗУ или SSD. Что отличает кэши, так это их задержка доступа и скорость. Несмотря на то, что оперативная память работает очень быстро, она на несколько порядков медленнее процессора. ОЗУ может потребоваться сотни циклов, чтобы ответить данными, и процессору будет нечего делать. Если данные не находятся в оперативной памяти, для доступа к данным на SSD могут потребоваться десятки тысяч циклов. Без кешей наши процессоры остановились бы.
Процессоры обычно имеют три уровня кэша, образующие так называемую иерархию памяти. Кэш L1 - самый маленький и быстрый, L2 - посередине, а L3 - самый большой и медленный из кешей. Над кэшами в иерархии находятся небольшие регистры, в которых во время вычислений хранится одно значение данных. Эти регистры на несколько порядков являются самыми быстрыми устройствами хранения данных в вашей системе. Когда компилятор преобразует высокоуровневую программу в язык ассемблера, он определяет наилучший способ использования этих регистров.
Когда ЦП запрашивает данные из памяти, он сначала проверяет, сохранены ли эти данные в кэше L1. Если это так, к данным можно быстро получить доступ всего за несколько циклов. Если его нет, ЦП проверит L2 и затем проведет поиск в кэше L3. Кэши реализованы таким образом, что они обычно прозрачны для ядра. Ядро просто запросит некоторые данные по указанному адресу памяти, и любой уровень в иерархии, на котором они есть, ответит. По мере того, как мы переходим к последующим этапам иерархии памяти, размер и задержка обычно увеличиваются на порядки. В конце концов, если ЦП не сможет найти искомые данные ни в одном из кэшей, только тогда они перейдут в основную память (ОЗУ).

В типичном процессоре каждое ядро будет иметь два кэша L1: один для данных и один для инструкций. Кэши L1 обычно составляют около 100 килобайт, и их размер может варьироваться в зависимости от чипа и поколения. Также обычно имеется кэш L2 для каждого ядра, хотя в некоторых архитектурах он может быть разделен между двумя ядрами. Кэши L2 обычно составляют несколько сотен килобайт. Наконец, существует единый кэш-память L3, которая используется всеми ядрами и имеет размер порядка десятков мегабайт.
Когда процессор выполняет код, инструкции и значения данных, которые он использует чаще всего, кэшируются. Это значительно ускоряет выполнение, поскольку процессору не приходится постоянно обращаться к оперативной памяти за нужными ему данными. Мы поговорим подробнее о том, как на самом деле реализованы эти системы памяти, во второй и третьей частях этой серии.
Помимо кэшей, одним из других ключевых строительных блоков современного процессора является точный предсказатель ветвления. Инструкции ветвления аналогичны операторам if для процессора. Один набор инструкций будет выполняться, если условие истинно, а другой - если условие ложно. Например, вы можете захотеть сравнить два числа и, если они равны, выполнить одну функцию, а если они разные, выполнить другую функцию. Эти инструкции ветвления чрезвычайно распространены и могут составлять примерно 20% всех инструкций в программе.
На первый взгляд, эти инструкции ветвления могут не показаться проблемой, но на самом деле они могут быть очень сложными для процессора. Поскольку в любой момент ЦП может одновременно выполнять десять или двадцать инструкций, очень важно знать, какие инструкции следует выполнять. Может потребоваться 5 тактов, чтобы определить, является ли текущая инструкция ветвью, и еще 10 тактов, чтобы определить, истинно ли условие. За это время процессор мог начать выполнять десятки дополнительных инструкций, даже не зная, были ли они правильными для выполнения.
Чтобы обойти эту проблему, все современные высокопроизводительные процессоры используют технику, называемую спекуляцией. Это означает, что процессор будет отслеживать инструкции ветвления и догадываться, будет ли выполнено ветвление или нет. Если прогноз верен, процессор уже начал выполнение последующих инструкций, что обеспечивает прирост производительности. Если прогноз неверен, процессор останавливает выполнение, удаляет все неправильные инструкции, которые он начал выполнять, и начинает сначала с правильной точки.
Эти предсказатели ветвлений являются одними из самых ранних форм машинного обучения, поскольку предсказатели изучают поведение ветвей по мере их прохождения. Если он предскажет неправильно слишком много раз, он начнет учиться правильному поведению. Десятилетия исследований методов прогнозирования ветвлений привели к точности более 90% в современных процессорах.
Хотя спекуляции предлагают огромный прирост производительности, поскольку процессор может выполнять готовые инструкции, а не ждать в очереди занятых, они также выявляют уязвимости в системе безопасности. Знаменитая атака Spectre использует ошибки в прогнозировании ветвлений и спекуляции. Злоумышленник будет использовать специально созданный код, чтобы заставить процессор спекулятивно выполнить код, который приведет к утечке значений памяти. Некоторые аспекты спекуляции пришлось переработать, чтобы предотвратить утечку данных, что привело к небольшому снижению производительности.
Архитектура, используемая в современных процессорах, прошла долгий путь за последние несколько десятилетий. Инновации и продуманный дизайн позволили повысить производительность и лучше использовать базовое оборудование. Однако производители процессоров очень скрывают технологии своих процессоров, поэтому невозможно точно знать, что происходит внутри. При этом основы работы компьютеров стандартизированы для всех процессоров. Intel может добавить свой секретный соус, чтобы увеличить количество попаданий в кэш, или AMD может добавить расширенный предсказатель ветвлений, но обе они выполняют одну и ту же задачу.
Часть 1: Основы компьютерной архитектуры
(архитектура набора инструкций, кэширование, конвейеры, гиперпоточность)
Часть 2: Процесс проектирования ЦП
(схемы, транзисторы, логические элементы, тактирование)
Часть 3: Схема и физическое построение чипа
(СБИС и изготовление кремния)
Часть 4: Текущие тенденции и будущие горячие темы в компьютерной архитектуре (Sea of Accelerators, 3D-интеграция, FPGA, вычисления в ближней памяти)
Это первое знакомство и обзор охватили большинство основ работы процессоров. Во второй части мы обсудим, как устроены компоненты, входящие в ЦП, рассмотрим логические вентили, синхронизацию, управление питанием, принципиальные схемы и многое другое.
Эта статья была впервые опубликована 22 апреля 2019 года. Мы немного изменили ее и изменили в рамках нашей инициативы ThrowbackThursday. Заголовок: электронная плата крупным планом, автор Raimudas