Параллельное программирование, процессоры и кэши: беседуют Кэйвон и Кунле

Прокомментировать Просмотры: 8

Обучаясь на четвёртом курсе в Стэнфорде*, я записался на практикум по параллельному программированию. Разобравшись с алгоритмами, структурами данных и математическим фундаментом нейросетей, я неожиданно осознал, что имею весьма смутное представление о внутреннем устройстве микропроцессоров. Мне захотелось детально погрузиться в компьютерную архитектуру, чтобы лучше понимать принципы работы «железа», на базе которого создаётся современный софт и которым мы пользуемся ежедневно.

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

Зачем это нужно?

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

Предлагаю сначала разобрать более сложные и злободневные темы, а затем вернуться к базису, который держится на трёх китах: 1. специализация аппаратного обеспечения; 2. центральные процессоры (CPU); 3. кэш-память и механизмы обеспечения её когерентности.

1. Специализация железа

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

Закон Деннарда перестал действовать в период с 2005 по 2007 год: простое уплотнение транзисторов в рамках одного процессорного ядра больше не приводит к линейному росту производительности из-за критического перегрева и проблем с теплоотводом. Именно поэтому в игру вступает следующее фундаментальное соотношение:

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

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

Приведённая выше схема демонстрирует, что на арифметические инструкции в CPU (которые и составляют основу компьютерных вычислений) уходит всего 6% (!) потребляемой энергии. В специализированных чипах этот показатель существенно выше. Каковы же обратные стороны такого подхода?

Чем уже специализация микросхемы (правая часть графика), тем более ограниченный класс задач она способна эффективно решать. Главная сложность кроется не столько в написании кода, сколько в экономической целесообразности: узкая специализация оправдана лишь при колоссальных объемах однотипных вычислений — например, матричных трансформаций при инференсе нейросетей. Ниже представлен фрагмент кода на Spatial (предметно-ориентированном языке для проектирования аппаратных ускорителей, расположенном на третьем месте слева на графике):

(Кстати, рекомендую познавательный видеоролик о массовом выпуске полупроводников: https://youtu.be/zyr-I9PdIac?si=FF_BIWIVzxUZB_tS.)

Теперь обратимся к универсальным процессорам из левой части схемы.

2. CPU

Современные персональные компьютеры и мобильные гаджеты по-прежнему базируются на центральных процессорах (CPU). Львиная доля программного обеспечения, создаваемого разработчиками на этой платформе, исполняется именно на них. Из каких базовых элементов они состоят?

Stanford* CS149
Stanford* CS149

Базовая архитектура включает три главных компонента: 1. Управляющий блок (Control Unit, выделен оранжевым), координирующий подачу инструкций на арифметическое устройство (для операций сложения, умножения и т.д.); 2. Арифметико-логическое устройство (ALU, жёлтого цвета), выполняющее непосредственные расчёты; 3. Регистры (синие блоки) — сверхбыструю оперативную память для временного хранения обрабатываемых данных и команд. В свою очередь, каждый из этих модулей построен на мириадах микроскопических транзисторов.

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

Stanford* CS149
Stanford* CS149

3. Кэш-память и протоколы когерентности

Тема кэширования стала одной из самых увлекательных в рамках курса CS149.

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

Напомним классическую иерархию памяти компьютера — от наиболее миниатюрных и быстрых накопителей к объемным и медленным:

Регистры → Кэш-память → Оперативная память → Накопитель (диск)

Типичный микропроцессор оснащен несколькими уровнями кэша. Возникает закономерный вопрос: как предотвратить появление противоречивых данных при параллельной работе с несколькими кэшами?

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

PrRd — Processor Read; PrWr — Processor Write; BusRd — Bus (шина) Read; BusRdX — Bus Read Exclusive; BusWB — Bus Writebac. A / Б — если кэш контроллер видит сообщение А, инициирует действие Б. Каждый кружок — состояние одной линии (места) кэша.
PrRd — Processor Read; PrWr — Processor Write; BusRd — Bus (шина) Read; BusRdX — Bus Read Exclusive; BusWB — Bus Writebac. A / Б — если кэш контроллер видит сообщение А, инициирует действие Б. Каждый кружок — состояние одной линии (места) кэша.

К слову, затронув тему эффективности памяти, нельзя не упомянуть любопытную научную разработку отечественных исследователей из МФТИ: https://mipt.ru/news/mgnovennaya-zagruzka-i-vechnoe-khranenie-v-mfti-sdelali-segnetoelektricheskuyu-pamyat-rekordno-vynos.

Учебный модуль длился десять недель, поэтому уместить весь материал в рамках одной статьи физически невозможно. За кадром остались такие темы, как векторные расширения SIMD, модели ISPC/SPMD, архитектура графических процессоров и CUDA, механизмы примитивов синхронизации и транзакционная память. Каждое из этих направлений заслуживает отдельного глубокого изучения!


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

 

Источник

Поделиться:

Похожие статьи

Поиск по играм, новостям и статьям…

Введите не менее двух символов

Введите не менее двух символов