В последнее время мы много говорим о производительности систем. Несколько дней назад компания Groq опубликовала новость о том, что она превзошла всех в тестах инференса LLM с помощью своего аппаратного language processing unit (LPU). Успех LPU обусловлен инновационным дизайном его аппаратной архитектуры в сочетании с мощным компилятором. Поскольку в последнее время мы фокусируемся на производительности, я думаю, что понимание того, что скрывается за аппаратным обеспечением и компилятором LPU, будет очень актуально для нашей дискуссии.
Groq не публиковали никаких статей о самом LPU, но в предыдущие годы они опубликовали две статьи (см. эту статью 2020 года и эту статью 2022 года ), объясняющие дизайн и реализацию своего тензорного потокового процессора (tensor streaming processor - TSP), а также то, как они создали механизм распределенного инференса с использованием TSP. Хотя официально об этом не заявляли, но LPU, скорее всего, основан на этой распределенной системе или ее расширении.
В этой статье мы собираемся разобрать архитектуру TSP и его компилятора, а затем увидим, как Groq построили надежный и высокопроизводительный распределенный механизм инференса ИИ с использованием этих TSP.
Архитектура TSP сильно отличается от архитектуры обычного процессора или графического процессора, главным образом для того, чтобы сделать аппаратное обеспечение TSP более детерминированным. Давайте сначала поговорим о том, что вызывает недетерминизм в процессоре или графическом процессоре.
Микроархитектура процессоров и графических процессоров имеет множество особенностей, которые делают выполнение инструкций на этих устройствах недетерминированным, т. е. вы не можете гарантировать, когда конкретная инструкция будет выполнена, сколько времени потребуется для ее завершения и когда будут доступны результаты.
Например, современный процессор имеет:
Суперскалярную архитектура : способна выполнять несколько инструкций за цикл.
Out-of-order execution: инструкции могут выполняться в любом порядке.
Спекулятивное выполнение: в случае ветвления он делает предположение о том, является ли условие ветвления истинным или ложным, и спекулятивно выполняет эту ветвь заранее, чтобы улучшить пропускную способность. В случае ошибки предсказания, он должен отменить результаты спекулятивного выполнения, чтобы выполнить другую ветку.
Конвейерная обработка инструкций: она делит выполнение инструкций на несколько этапов и выполняет их конвейерным способом. Таким образом, когда одна инструкция переходит на второй этап конвейера, ЦП может поместить новую инструкцию на первый этап конвейера. Это снова улучшает пропускную способность инструкций.
Несколько уровней кешей: процессоры имеют 2–3 уровня кешей, чтобы сократить задержку инструкций, если им необходимо загрузить данные из памяти.
Все это делает порядок и время выполнения инструкций в ЦП недетерминированными, и о них трудно рассуждать. У графических процессоров также есть несколько источников недетерминированности, таких как кэши, общая и глобальная память, динамическое разделение ресурсов.
Проблема с этим присущим этим устройствам недетерминизмом заключается в том, что из-за него трудно рассуждать о производительности программы и трудно гарантировать худший предел производительности.
В результате компания Groq разработала радикально новую конструкцию своего тензорного потокового процессора (TSP), которая имеет массивный параллелизм, но нулевое недетерминированное поведение. Они упростили аппаратное обеспечение, предоставив компилятору больше возможностей и контроля над точным планированием и выполнением инструкций, чтобы гарантировать производительность программы. Давайте разберемся, как выглядит архитектура TSP изнутри.
Аппаратная конструкция TSP резко контрастирует с конструкцией процессора или графического процессора. Обычный многоядерный чип имеет тайловую архитектуру, где каждый тайл представляет одно процессорное ядро. Внутри каждый тайл состоит из набора функциональных блоков, отвечающих за выполнение различных видов вычислений. Например, арифметические операции, операции с памятью, логические операции, управление инструкциями и так далее.
Дизайнеры TSP вывернули эту традиционную конструкцию наизнанку. Они переместили функциональные блоки за пределы ядер и расположили их в виде двухмерной сетки. Каждый столбец этой сетки содержит функциональные единицы определенного типа и называется срезом. На следующей диаграмме показана тайловая конструкция обычного многоядерного чипа и TSP рядом.
TSP имеет следующие функциональные части:
MXM : для выполнения матричных операций.
SXM : для операций сдвига и поворота векторов.
MEM : операции чтения/записи памяти.
VXM : арифметические операции с векторами.
ICU : блок управления инструкциями — в отличие от других срезов, он организован горизонтально и отвечает за выборку и отправку инструкций для выполнения в других срезах.
Поняв архитектуру TSP, давайте переключим внимание на его основной режим работы — выполнение инструкций. Именно здесь инженерное мастерство Groq по-настоящему проявляется, демонстрируя, как конструкция оборудования напрямую определяет возможности исполнения.
TSP выполняет инструкции в режиме SIMD (одна инструкция - много данных). Каждый функциональный срез состоит из 20 тайлов, и каждый тайл способен создавать 16 элементов вектора. Таким образом, полный срез может работать и создавать векторы с максимальным размером 320 элементов.
Эти срезы взаимодействуют друг с другом по принципу производитель-потребитель: один срез выдает результаты в виде потока, а другой получает этот поток в качестве входных данных и работает с ним.
Когда вектор считывается из памяти, ему присваивается идентификатор потока (от 0 до 31) и направление потока (восток или запад). Поток течет от одного фрагмента к другому. Каждый срез может обрабатывать поток и создавать новый результирующий поток, либо он может позволить потоку течь как есть к следующему соседнему срезу.
Поскольку каждый тайл в TSP способен обрабатывать 16 элементов векторного потока, для эффективной обработки полного вектора выполнение инструкций является конвейерным. По временной метке t1
, ICU выдает инструкцию самому нижнему фрагменту одного из функциональных фрагментов. При следующей временной метке выходные данные этого тайла перемещаются на север в следующий срез, в то время как ICU выдает еще одну инструкцию для следующей части вектора самому нижнему тайлу. Это приводит к поэтапному выполнению инструкций, как показано на диаграмме ниже:
Точность, с которой TSP выполняет инструкции, зависит не только от его аппаратного обеспечения, но и от синергетического взаимодействия с компилятором. Изучение компилятора и архитектуры набора команд показывает, как программное и аппаратное обеспечение объединяются для оптимизации производительности.
Разработчики TSP упростили аппаратное обеспечение и перенесли многие сложности, связанные с выполнением инструкций, в компилятор. Компилятору необходимо точно запланировать инструкции и поток данных, чтобы правильно выполнить данную программу и сделать это максимально эффективным способом.
Компилятор имеет доступ к следующему архитектурно видимому состоянию оборудования TSP:
Абстракция 320-полосного программирования: каждая ячейка в чипе TSP способна работать с 16 элементами вектора (16 полос) в режиме SIMD. Вертикальный срез состоит из 20 таких тайлов, таким образом, всего для исполнения доступно 320 полос SIMD.
144 независимых очереди инструкций: на чипе имеется 144 очереди инструкций, каждая из которых способна выдавать одну или несколько инструкций за цикл. Компилятор имеет полный контроль над порядком выполнения программ в каждой из этих очередей.
64 логических потока на полосу: каждая полоса имеет доступ к 64 логическим потокам, которые можно использовать для перемещения операндов или результатов. Они разделены между направлениями Восток и Запад, т.е. 32 из них могут использоваться для перемещения данных на Восток, а остальные 32 — для перемещения данных на Запад.
220 Мбайт глобальной общей SRAM
Поскольку в оборудовании TSP нет недетерминированного поведения, компилятор имеет точные сведения о задержке каждой инструкции. Кроме того, компилятор обладает полной информацией о потоке данных в компилируемой программе (например, о графе вычислений глубокой нейронной сети).
Компилятор выявляет зависимости между вычислительными задачами и на основании этого назначает их для параллельного выполнения на доступных функциональных блоках TSP.
Он планирует инструкции таким образом, что поток достигает среза как раз тогда, когда срез готов его обработать. Компилятор может сделать это, поскольку он точно знает задержки инструкций и направления потока данных.
Концептуально компилятор решает двумерное планирование инструкций и данных как во времени, так и в пространстве. Модель программирования TSP опирается на два важнейших элемента:
Детерминированный путь данных в оборудовании
Предоставление временной информации о задержке инструкции через ISA
Благодаря этому серверная часть компилятора может отслеживать положение и время использования любого потока на кристалле. В документе это называется «программно-определяемым оборудованием».
До сих пор мы рассмотрели архитектуру TSP и узнали, что она полностью параллельна на аппаратном уровне, но лишена каких-либо недетерминированных функций. Компилятор выполняет всю тяжелую работу по планированию инструкций и потока данных таким образом, чтобы различные функциональные фрагменты обрабатывали векторные данные в форме потоков в режиме SIMD.
Это превращает TSP в основную единицу языкового процессора (LPU). Несколько TSP объединены в виде стойки, и многие такие стойки соединены вместе, образуя распределенную систему, способную обеспечивать огромную пропускную способность. Давайте разберемся, как Groq построил распределенную систему из TSP и как она работает.
Как и в случае с TSP, цели проектирования распределенной системы с несколькими TSP также связаны с детерминированным потоком данных и выполнением инструкций, а также с малой задержкой связи между узлами.
Проектирование распределенной системы TSP начинается с узла. Узел формируется из 8 устройств TSP, заключенных в шасси. Каждое из этих устройств состоит из 11 контактов, из которых 7 контактов используются для подключения каждого устройства TSP к другим 7 устройствам TSP в узле; а остальные 4 контакта используются для формирования глобальной связи.
Поскольку каждое устройство в узле имеет 4 глобальных канала, всего имеется 32 глобальных канала, которые вместе образуют маршрутизатор с 32 виртуальными портами.
Маршрутизатор поддерживает большое количество соединений, высокую пропускную способность и высокую производительность — именно то, что нужно высокопроизводительной распределенной системе.
Девять таких узлов TSP по восемь TSP в каждом объединяются в стойку. Поскольку каждый узел в стойке имеет 32 порта, общая стойка имеет 32 x 9 = 288 глобальных портов. 144 из этих портов используются локально для обеспечения полной связи между узлами для быстрой передачи данных внутри стойки, а остальные 144 порта используются для подключения к другим стойкам.
Система с максимальной конфигурацией может поддерживать 145 таких стоек, соединенных между собой, состоящих из 10 440 TSP, с не более чем 5 переходами между любыми двумя TSP в системе.
В этом режиме масштабируемой распределенной системы функциональные блоки одного TSP действуют как одно вычислительное ядро массивного параллельного процессора. Вычислительная модель TSP основана на детерминированном аппаратном обеспечении, и то же поведение должно быть распространено и на режим распределенной системы.
Существует ряд механизмов для достижения такого детерминизма и синхронизации отдельных блоков TSP в крупнораспределенной системе. Давайте посмотрим, что они из себя представляют.
Каждое устройство TSP содержит аппаратный счетчик, называемый счетчиком c аппаратной синхронизацией (hardware aligned counter - HAC), с периодом переполнения 256 циклов. TSP используют это для синхронизации друг с другом, выполняя следующие шаги:
Когда два TSP соединены между собой, один из них передает свое значение HAC своему партнеру. Получив значение, партнер отправляет это значение обратно отправителю. Отправитель наблюдает разницу между своим текущим значением HAC и значением, которое он получил обратно. Эта разница представляет собой задержку соединения между двумя устройствами. Этот процесс повторяется несколько раз, чтобы установить среднюю задержку соединения между двумя TSP.
После этого два устройства располагаются в отношениях «родитель-потомок». Родитель T0
периодически отправляет свое текущее значение HAC ( HAC0
) ребенку T1
. Ребенок добавляет среднюю задержку соединения к HAC0
и сравнивает его со своим собственным значением HAC. Разница между двумя значениями представляет собой первоначальное рассогласование из-за постоянного отклонения тактового сигнала. T1
корректирует значение HAC, чтобы уменьшить эту разницу. После повторения этого процесса несколько раз значения HAC двух TSP сходятся в пределах небольшой окрестности, которая представляет собой джиттер задержки канала.
Этот протокол позволяет двум TSP синхронизироваться друг с другом, и его можно расширить до многоинтервальной сети TSP, установив в сети родительские/дочерние TSP связующего дерева.
Прежде чем запускать программу для выполнения в системе с несколькими TSP, все TSP необходимо выровнять, чтобы правильно запланировать поток данных и выполнение инструкций в системе. Для этого используются следующие механизмы.
На уровне отдельного TSP имеется несколько независимых функциональных блоков и 144 независимых очереди инструкций. Прежде всего, их необходимо синхронизировать. Для этого TSP поддерживает SYNC
и NOTIFY
инструкции барьерной синхронизации. В этой схеме SYNC
инструкция переводит все очереди инструкций в припаркованное состояние, и одна из очередей действует как уведомитель. Когда уведомитель выдает NOTIFY
инструкцию, она транслируется во все очереди на чипе, после чего они синхронизируются и возобновляют работу.
В то время как SYNC
/ NOTIFY
механизм работает для одного TSP, для системы с несколькими TSP требуется дополнительная работа. Два TSP используют HAC для синхронизации друг с другом, кроме того, каждый TSP поддерживает DESKEW
инструкцию. Когда функциональный блок выполняет DESKEW
инструкцию, он прекращает обработку любых последующих инструкций до тех пор, пока HAC TSP не переполнится (граница эпохи). Это гарантирует, что следующая инструкция будет выполнена TSP на границе эпохи. Два TSP могут согласовать начало выполнения программы, используя следующий механизм:
Дочерний TSP включает себя в цикл опроса, где в каждую эпоху он проверяет поступление вектора от родительского TSP.
Родительский TSP начинает выполнение программы, сначала выполняя DESKEW
инструкцию, которая гарантирует, что первая инструкция программы будет выполнена на границе эпохи.
После DESKEW
, родительский TSP передает вектор дочернему TSP на границе эпохи. Дочерний TSP при получении вектора сохраняет его в буфере.
На границе следующей эпохи дочерний TSP выходит из цикла опроса и выполняет RECEIVE
инструкцию для обработки вектора.
Эта схема гарантирует, что два TSP правильно выровнены в начале выполнения программы.
Для масштабирования многошаговой системы эту схему можно многократно выполнять на каждом шаге связующего дерева.
Хотя TSP выполняют однократную синхронизацию в начале программы, их также необходимо повторно синхронизировать во время выполнения программы, поскольку каждый TSP имеет свой собственный независимый источник синхронизации.
Для этого TSP используют облегченную схему. Помимо HAC, каждый TSP имеет счетчик с программной синхронизацией (SAC), который имеет тот же цикл переполнения, что и HAC.
Однако SAC не синхронизированы между TSP, SAC просто подсчитывает тактовые циклы TSP. Значение HAC представляет глобальное время распределенной системы, а значение SAC представляет собой местное время. Следовательно, разница между значениями HAC и SAC определяет накопленный дрейф.
Чтобы повторно синхронизировать локальное и глобальное время, TSP выполняет RUNTIME_DESKEW
инструкцию. Эта инструкция принимает единственный параметр: количество циклов до остановки. Каждый TSP в системе выполняет RUNTIME_DESKEW
команду одновременно и останавливает ее выполнение, чтобы скорректировать глобальное время с местным временем на основе накопленного отклонения.
Как мы видели, гарантии детерминированного выполнения распространяются также и на распределенную систему с несколькими TSP. Это позволяет компилятору иметь сведения с точностью до цикла о перемещении данных внутри TSP, а также по всей сети. Компилятор знает точное время введения вектора в исходный TSP и точное время, когда он достигнет целевого TSP. Это называется программно-планируемой сетью, поскольку вместо аппаратного динамического управления потоком данных компилятор разрешет все статически во время компиляции.
Давайте обсудим характеристики распределенной системы TSP, которые позволяют компилятору выполнять работу в сети с программным планированием.
Для моделей глубокого обучения компилятор может определить поток данных на основе статического графа вычислений модели. Компилятор также автоматизирует распределение вычислительных задач по доступным устройствам TSP в сети. В результате компилятор вычисляет точное время выполнения каждой подзадачи и обмен активациями между уровнями. Это делает шаг параллельной декомпозиции явным и полностью контролируемым компилятором.
В обычной сетевой системе поток пакетов через сеть управляется аппаратным обеспечением, которое оптимизирует маршруты, когда обнаруживает проблемы в сети. Эта реактивная корректировка потока данных увеличивает задержку и вносит недетерминированность в поток данных.
Чтобы избежать этого, распределенная система с несколькими TSP явно планирует поток данных через сеть с помощью своего компилятора. Компилятор разумно маршрутизирует данные таким образом, чтобы в любой момент времени в сети не возникало перегрузки.
Помимо этого, поток данных, запланированный компилятором, также уменьшает задержку в сети, поскольку вместо того, чтобы устройство запрашивало часть данных от другого устройства, компилятор может запланировать, чтобы второе устройство активно отправляло данные на первое устройство именно тогда, когда ему это необходимо работать с этими данными.
Еще одним преимуществом планирования потока данных во время компиляции является то, что оно позволяет компилятору эффективно балансировать нагрузку потока по доступным каналам. В обычной сети оборудование принимает решения о маршрутизации для каждого пакета в зависимости от показателей перегрузки, доступных в маршрутизаторе.
Однако в случае системы с несколькими TSP компилятор оптимально выполняет это планирование на основе объема данных (т. е. произведения измерений тензора H x W x C
) и выбирает количество ссылок, по которым распределяется трафик. Это эффективно использует доступную пропускную способность системы и снижает общую задержку.
До сих пор мы обсуждали, что делает оборудование LPU быстрым, давайте в заключение обсудим, как оборудование обрабатывает ошибки.
В традиционной сетевой системе ошибки передачи обрабатываются на канальном уровне путем простой повторной попытки передачи пакетов. Эта стратегия нежизнеспособна для системы с несколькими TSP, поскольку она приводит к недетерминированным задержкам. Вместо этого система с несколькими TSP обрабатывает ошибки, используя стратегию прямого исправления ошибок.
В этой стратегии пакеты передаются шаг за шагом, и любые ошибки исправляются на месте. Любые критические ошибки помечаются, чтобы среда выполнения могла воспроизвести инференс. Стратегия воспроизведения обрабатывает любые случайные ошибки, но постоянные ошибки требуют ручного вмешательства.
В целях обеспечения надежности каждая стойка состоит из запасного узла TSP. Среда выполнения контролирует работоспособность всех узлов в стойке и переключается на резервный узел, когда обнаруживает неисправимые ошибки на одном из узлов.
Использование программного повтора вывода и резервного узла позволяет системе корректно восстанавливаться после любых критических ошибок.
На этом мы завершаем обсуждение архитектуры аппаратного обеспечения, лежащего в основе LPU Groq. Мы рассмотрели довольно много подробностей о TSP и о том, как Groq расширили его в форме распределенной системы с массовым параллелизмом. Давайте быстро подведем итоги.
Инновационный дизайн: LPU Groq, построенный на базе тензорного потокового процессора (TSP), демонстрирует отход от традиционных архитектур CPU/GPU, уделяя особое внимание детерминированному и предсказуемому выполнению для повышения производительности задач вывода языковой модели.
Детерминированное выполнение. Архитектура TSP обеспечивает детерминизм за счет упрощения аппаратной конструкции, что позволяет компиляторам точно планировать инструкции и поток данных, что имеет решающее значение для предсказуемой производительности.
Программно-планируемая сеть. Подход Groq к созданию распределенной системы с несколькими TSP устраняет недетерминизм, который обычно возникает при маршрутизации и планировании в традиционных сетевых системах. Компилятор играет решающую роль в перемещении данных, обеспечивая эффективность и предсказуемость.
Массивный параллелизм: благодаря сочетанию аппаратного дизайна и интеллекта компилятора TSP, LPU поддерживает высокопараллельные операции, что делает его особенно подходящим для больших языковых моделей (LLM).
Масштабируемость и надежность системы . Физическая и логическая конструкция сети TSP обеспечивает масштабируемость, поддерживая систему из более чем 10 000 TSP с минимальной задержкой и высокой надежностью, включая стратегии исправления ошибок и механизмы аварийного переключения.
Потенциальное влияние : детерминированный характер и масштабируемая конструкция технологии LPU обещают переопределить ожидания производительности для вычислительных задач, особенно в области искусственного интеллекта и машинного обучения, устанавливая новый стандарт для будущих разработок процессоров.
Недавно мы провели прямую трансляцию, обсуждая конструкцию TSP и LPU и анализируя, как они работают. Посмотрите запись здесь