На четвертом курсе Стэнфорда* я записался на урок по параллельному программированию. Изучив алгоритмы, контейнеры данных и математику, стоящую за нейронными сетками, понял, что толком не знаю, что такое микропроцессор. Было очень интересно углубиться в компьютерную архитектуру и понять лучше, как работает то, для чего мы разрабатываем софт и чем пользуемся ежедневно.
Преподаватели были одни из лучших в моём опыте в Стэнфорде*. Кэйвон Фатахалиан—эксперт в области графики и производительности, а Кунле Олукотун—пионер в создании многоядерных микропроцессоров.
Зачем это всё нужно?
Если кратко—для высшей производительности и экономии ресурсов, включая денег. В эру прикладного ИИ, темы архитектуры железа (аппаратного обеспечения / hardware) вирусятся всё больше среди разработчиков, так как он и энергия стали bottleneck-ом.
Попробую сначала обсудить темы посложнее и релевантнее, а потом описать азы, что приводит к порядку: 1. специализация железа; 2. СPU; 3. кэши и их когерентность.
1. Специализация железа
Учитывая уйму денег, вложенную в индустрию ИИ, малейшая экономия энергопотребления и времени при инференсе на серверах (напр, при запросе в Алису) много чего значит. Поскольку инференс нейронной сетки—ни что более, чем много вычислений череды линейных и нелинейных трансформаций (что я опишу подробнее в статьях про CS224N: Обработка Естественного Языка с Глубокими Нейронными Сетями и MATH104: Прикладная Теория Матриц), стало производиться железо, специально заточенное под вычисления ИИ.
Закон Деннарда перестал работать 2005-07 годах, т.е. при пичканье бóльшего числа транзисторов в ядро процессора, производительность не растёт прямо пропорционально из-за перегрева и рассеивания мощности. Поэтому в игру выходит данное несложное уравнение:

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

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

Чем специализированнее (справа на графе выше) железо, тем меньше область софта, подходящего для него. Сложность здесь далеко не в написании кода, а в подборе применения: с точки зрения финансирования, специализация подходит только для огромных применений похожих вычислений, напр. трансформаций в инференсе в нейронных сетях. Вот пример программы на языке Spatial, DSL для дизайна акселератора (третий слева на схеме выше):

(Кстати, любопытное видео про массовое производство микропроцессоров: https://youtu.be/zyr-I9PdIac?si=FF_BIWIVzxUZB_tS.)
Теперь рассмотрим микропроцессор в левой части графы.
2. CPU
Современные смартфоны и компьютеры до сих пор работают на CPU (Central Processing Unit). Большинство программ, разработанных программистами на этом сайте выполняются на CPU. Из чего он состоит?
Самый простецкий—из трёх частей: 1. CU (Control Unit) рыжего цвета, ответственный за подбору инструкции для ALU (умножение, сложение, т.д.); 2. ALU (Arithmetic & Logic Unit) жёлтого цвета, ответственный за сами вычисления; и 3. регистры (синего цветы), сверх-быстрая память для краткосрочного хранения инструкций и данных, над которыми работает ALU. В свою очередь, все три части сделаны из множества микроскопических транзисторов.
Архитектура CPU бывает совсем разной, включая параллельную, где арифметика может выполнятся разными ALU одновременно (не путать с многопоточностью), и все выводы пишутся в одну память (и в кэши, о которых ниже).
3. Кэши и протоколы когерентности
Ещё в CS149 мне очень понравилось изучать кэши.
Кэш—место для временного хранения данных, как регистры и память. Он деталь реализации аппаратного обеспечения, не влияющая на корректность программы, только на её производительность. Как и спец. железо, кеши экономят уйму ресурсов: время, энергию, пропускную способность шины, передающей данные из памяти и в неё.
Напомним себе иерархию хранения данных в компьютере, от маленькой и быстрой до большой и медленной:
Регистры → Кэши → Память → Диск
В простом микропроцессоре кэшей обычно несколько. Когда данные пишутся в один, как избежать противоречивости, читая из другого, особенно когда у нас параллелизм?
Для этого и существуют протоколы когерентости. Иными словами, когерентность—синхронизация. Протокол выполняет каждый кэш в ответ на чтение/письмо процессора и сообщения от других кэшей. Есть разные протоколы, MESI один из них, если интересно поизучать:
Кстати, насчет памяти и её эффективности, что я не затрагивал в этой статье—вышли интересные исследования от учёных из МФТИ: https://mipt.ru/news/mgnovennaya-zagruzka-i-vechnoe-khranenie-v-mfti-sdelali-segnetoelektricheskuyu-pamyat-rekordno-vynos.
Класс длился 10 недель, так что весь контент не уместить в одну статью. Не затронул темы SIMD, ISPC/SPMD, GPU и CUDA, замки, транзакционная память. Про них тоже захватывающе почитать!
* Внесён в перечень иностранных и международных организаций, деятельность которых признана нежелательной на территории РФ.
ссылка на оригинал статьи https://habr.com/ru/articles/1065658/