Марков цепи пропил
2.64K subscribers
209 photos
30 videos
1 file
69 links
Download Telegram
1😢3416😁9🔥1💩1🤡1
Марков цепи пропил
Red eyes is all you need, или пихаем LLM в FPGA Вдохновился недавней новостью, о том, что LLM зашили в железо, и решил попробовать повторить в меньших масштабах, написав проект на verilog, где ~854K модель зашивается в Artix-7 (XC7A200T). Задачей было уложиться…
Продолжение red eyes is all you need

В общем, глобально было два пути оптимизации этого исчадия: через увеличение тактовой частоты железки и через уменьшение tokens per cycle. Первое в самом начале решалось простым раскидыванием регистров в нужных местах, но со временем я уперся в тот факт, что один большой BRAM модуль с весами слишком медленно доставляет данные из-за физического положения (веса/активации нужны многим модулям, и сам путь до нужного модуля занимает слишком много времени). Поэтому пришлось дробить на кучу блоков + конфигурировать все модули в свои pblocks.

Идея в том, что place&route по умолчанию раскидывает логику по кристаллу как ему удобнее, а удобнее ему обычно не там, где надо, и clock tree synthesis потом героически пытается развести тактовый сигнал через всю эту кашу. Если matvec u_qkv оказался в одном углу, а его weight_store в другом, то на трассы между ними уходят те самые наносекунды, из-за которых слайсится тайминг. Поэтому каждому matvec'у выделяется своя территория с примерно нужным количеством SLICE/DSP/BRAM, и его weight_store селится туда же.

Нижняя половина кристалла (X0..X145, Y0..Y149) поделена на четыре вертикальные полосы под четыре w32-matvec'а внутри transformer_layer: u_qkv в X0..X41, u_proj в X42..X69, u_ff_up в X70..X109, u_ff_down в X110..X145. Каждый pblock включает и сам matvec, и парный к нему weight_store, чтобы трассы от выхода weight_store (256 бит на u_qkv/u_proj/u_ff_up/u_ff_down, 128 на u_head_proj) не тянулись через полкристалла. Размеры полос пропорциональны размеру весов: pb_ff_up жирнее pb_proj по BRAM, потому что ff_up хранит 512x128 против 128x128 у proj, и так далее.

Head projection ушел на правый край (X146..X163) во всю высоту кристалла, потому что он живет в transformer_top, и его удобно держать на отшибе - он шарит tok_emb с embedding lookup, и весь этот weight-tied кусок логически отдельный. В верхней половине (Y150+) поселились остальные. Слева ln_f (X0..X100), правее в полосе X100..X145 живут u_sm_a и u_sm_b в одном pblock'е (чтобы оба softmax'а были рядом со своими ping-pong буферами), а сэмплер сидит в той же X-полосе сверху и снизу от softmax'а - в Y150..Y169 и Y211..Y249, обтекая его.

Благодаря этому удалось достичь Fmax ~98.8 MHz и зафиксировать рабочую частоту на 95 MHz
1🔥152
Media is too big
VIEW IN TELEGRAM
Дальше пошла оптимизация cycles/token

Тут вся работа свелась к тому, чтобы attention перестал быть последовательным куском логики, потому что именно он съедал большую часть циклов.

Первое что переехало это сами matvec'и. Раньше matvec_fp16 ел по одному int8-весу за такт через 8-битный weight bus, и QKV с proj шарили этот bus через мультиплексор. Сейчас стоит matvec_fp16_w32 (и w16 для head_proj), который читает по 32 веса за такт из своего weight_store, а qkv и proj получили свои отдельные банки и могут работать параллельно, не толкаясь за один порт.

Дальше пошел score внутри attn head (Q*K). K cache отдает 4 fp16-позиции за одно чтение, над ним стоят четыре fp16_mul, которые делят один q_head[dim], и за такт получаем 4 произведения Q*K вместо одного. Дальше эти произведения нужно складывать по dim, и тут возникает обычная для fp16-арифметики проблема: fp16_add имеет 4-тактную обратную связь, и если складывать в один аккумулятор, то получается одно сложение раз в 4 такта. Поэтому вместо одного аккумулятора стоят 16 fp16_reduce_k8 в 4-way ping-pong (4 позиционных lane'а на 4 фазы pair_idx). A берет каждый четвертый quad (0, 4, 8...), B следующий (1, 5, 9...), C (2, 6, 10...), D (3, 7, 11...). Пока A досчитывает свой quad, B уже принимает следующий, потом C, потом D, и к моменту когда снова доходит очередь до A, его 4-тактная задержка уже прошла. Так держится throughput одно сложение за такт на lane.

AV устроен похоже, четыре fp16_mul читают 4 V-вектора из одного 64-битного чтения, дальше balanced add tree и один аккумулятор av_acc[d].

Самое жирное это параллелизм между attn heads. Инстансов softmax два (u_sm_a для четных, u_sm_b для нечетных), и за ними два ping-pong attn_buf'а. Score sub-FSM и AV sub-FSM крутятся независимо: пока AV ест выходы attn head N из attn_buf_a, score уже стреляет dot-product'ами для attn head N+1 в u_sm_b, который пишет в attn_buf_b. K и V в кэше имеют раздельные адреса чтения ровно для этого, чтобы score (читает K) и AV (читает V) не дрались за порт. Pipeline depth ограничен через scored_count - av_done_count <= 2, чтобы score attn head N+2 не начал перезаписывать буфер, который AV еще читает для attn head N.

Из мелкого еще дописал нормальный сэмплер вместо argmax'а: логиты умножаются на inv_temp с repetition penalty, уходят в softmax, top-k выбирается через compare tree, дальше multinomial draw через CDF-walk. Источник энтропии 16-битный Fibonacci LFSR (потому что Mersenne Twister и Philox на один сэмпл в такт это перебор для такой модели мне было лень).

Тащем-то, ответ на главный вопрос: “сколько токенов”? Эта чилслодробилка выдает примерно 1100 токенов в секунду на 854k W8A16, но потенциально может и больше, если добавить еще параллельных вычислений и обработки весов за раз
1🔥266
Хз, какие выводы можно из этого сделать. Если хотите запихнуть модель в железо:

1) Используйте быструю память (медленную не используйте)
2) Делайте параллельные вычисления (последовательные не делайте)
1😁71🙏9🔥8
Forwarded from quant barbie
1😁287
С одной стороны мне не нравятся тонны нейрослопа, цены на оперативку/гпу и прочее, но с другой - вместо AI bubble у нас было бы вот это с теми же условиями
1😁523👻1💅1
This media is not supported in your browser
VIEW IN TELEGRAM
My honest reaction, когда любители сырков по средам фиксят vp8 туннель вместо многолетних багов в своей супераппе
1😁33😢188
В общем там еще headless vk решил отвалиться. Ловите фиксы 🥱

https://github.com/kulikov0/whitelist-bypass/releases/tag/v0.2.2
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥33👻32
О, прикольно, мы пробили отметку в 1к звезд на гитхабе. В честь этого раскачу major update в ближайшие дни, накидаю версию для Ирана (не ожидал, что попросят), и пойду дальше ботать и смотреть аниме, пока что-нибудь глобально не отвалится ☕️
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥66😁73
whitelist-bypass v0.3.0 - ночное, бессонное

Добавлена поддержка wbstream: DC + VP8 (авторизация/куки не нужны).

Добавлена обфускация пакетов (в принципе давно пора, но до определенного времени всем, думаю, было плевать на подобные проекты, поэтому особого значения не придавал) - надеюсь, что поможет с низкими скоростями при включенных бс. Плюсом добавлен контроль для VP8 bandwidth - в настройках появился пункт VP8 pacing: там выбирается количество кадров в секунду + количество пакетов, которые летят между кадрами. По умолчанию 24 фпс/30 батчей - у меня на тестах это самый стабильный вариант с точки зрения потери пакетов.

Из-за этого, правда, оно теперь работает только в режиме headless <-> headless (раньше была поддержка headless <-> webview).

По вашим просьбам было добавлено подключение к уже существующему звонку для headless + сделан headless VK bot (возможно на определенных системах он будет криво спавнить фоновые процессы со звонками, но проверить возможности нет).

А, еще были добавлены headless joiner'ы для линуксовых систем (wb, tm) - теперь в теории можно развернуть все это на слабых железках и попробовать пропускать весь траффик system-wide через redsocks/tun2socks.

Чуть более подробно расписал в [SETUP.md];
И сам релиз как всегда [здесь];
В случае, если что-то внезапно работает только на моей машине - по классике, пинайте меня в [чате канала]. Поправлю в нынешнем релизе, пока окончательно не переключился на другое
10🔥6221
1😁67😢542
Тяжелая неделя. Думал, что закончу с релизом примерно к среде-четвергу, и успею добавить несколько feature-request'ов, но, видимо, у судьбы были другие планы.

В общем, вроде +/- стабилизоровал билд и добавил новый сервис как запасной вариант. Не знаю, через сколько времени придется писать очередные костыли поверх новой апишки, поэтому на всякий пожарный лучше заведите левые аккаунты на нескольких платформах.

Ну и еще успел накидать аналогичный проекта для Ирана [тык]

Пока возьму передышку до выходных - хочу накатать пару постов на отвлеченные темы. А там, думаю, завезу пару вещей, о которых просили в issues и [чате канала].

Релиз все [там же] 😋
This media is not supported in your browser
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
1.03K🔥38🙏53
Да мы уже поняли, что вышла новая модель, которая на полпроцента лучше в тестовых бенчах
1😁64🔥14
Про Groq LPU и dataflow

Как говорится: “сначала маленькая историческая справка”. Dataflow-архитектуры это концепция, которую сформулировал Джек Деннис в MIT ещё в 1974. Идея простая: процессор не исполняет программу как последовательность инструкций, а гоняет данные через сеть вычислительных узлов. Узел запускается ровно тогда, когда на всех его входах накопились операнды, и порядок исполнения определяется готовностью данных, а не счётчиком команд. Сама идея идет в противовес фон Неймановской машине с регистровым файлом и указателем на следующую инструкцию. В 80-х под это пилили реальные машины (Manchester Dataflow, MIT Tagged-Token, Monsoon), и все они померли, потому что фон Нейман + кэш + спекулятивное исполнение оказались дешевле и универсальнее. Идея ушла в спячку и осталась жить в нишах вроде систолических массивов, FPGA-пайплайнов, DSP и т.п. А через 40+ лет ее снова стали реанимировать, потому что наконец появился новый клаас задач, на который эта архитектура хорошо ложится.

Что это за класс задач. Инференс LLM на batch=1 упирается в memory bandwidth: на каждый сгенерированный токен надо прочитать все веса модели целиком и прогнать через них один входной вектор. Например, Llama-70B в fp16 это 140 ГБ весов, и эти 140 ГБ надо протащить из памяти в матричные юниты один раз на токен. На современном GPU с HBM3 на ~3 ТБ/с теоретический потолок 3000/140 ~ 21 ток/с, и никакие TFLOPS тензорных ядер не спасут, потому что matmul вырождается в matrix-vector ([1, hidden] x [hidden, hidden]), и использование тензорных ядер падает до однозначных процентов. Они большую часть времени простаивают в ожидании данных.

Под эту нишу и заточен Groq. Так как модель исполнения другая, из чипа уходит большая часть привычной GPU инфраструктуры. HBM не нужна, потому что веса целиком держатся в on-chip SRAM. Кэши не нужны, из-за той же SRAM с равномерным доступом => иерархию строить незачем. Warp scheduler не нужен, потому что нет конкурирующих за вычислители потоков. Остаются функциональные блоки: MXM для матриц, VXM для векторов, SXM для пермутаций, MEM для банков SRAM. Они разложены полосами, и данные физически текут через них с фиксированной скоростью, один шаг полосы за такт. Всё расписание фиксируется на этапе компиляции: компилятор знает, что веса лежат в таком-то банке SRAM, активация окажется напротив MXM на такте T, и расставляет операции так, чтобы под каждым юнитом в каждый такт был нужный тензор. В рантайме железо просто исполняет план.

В такой архитектуре кратно возрастает пропускная для весов. SRAM сидит прямо рядом с MAC-ами, доступ занимает фиксированное число тактов, без промахов кэша и без очередей к контроллеру памяти. По пропускной способности это на порядок выше любой HBM (в текущем поколении LP30 порядка 150 ТБ/с против 20 ТБ/с на стек HBM3e). MXM-массивы не простаивают, потому что операнд гарантированно приедет в нужный такт по расписанию.

Но у этого подхода есть ряд недостатков. SRAM маленький, поэтому модель приходится размазывать на много чипов в детерминированной сети + компилятор планирует ещё и межчиповые передачи такт в такт. Под обучение всё это в принципе не годится: там нужны большие батчи, динамические графы, бэкпроп и прочее. Плюс вся сложность размещения, шедулинга и межчиповой синхронизации переехала в компилятор, и под каждую модель надо перекомпилировать весь граф
125🔥13👌4
Weekly update - v0.3.4

Пока промежуточный билд в рамках большого обновления. Сделан полный редизайн андроида; теперь выглядит +/- по-человечески.

Добавлено переподключение в случае утери связи/перехода с wifi на cellular для всех платформ.

Для wbstream'a реанимирован dc режим. Так же (пока только для wbstream) по просьбам появилась настройка vp8 -> dual track, чтобы получить x2 скорость в режиме data over video. По дефолту отключено, и я настойчиво прошу не нагружать инфраструктуру без крайней нужды кучей трафика.

Также появилась возможность задать интерфейс для socks5 - если нужно раздать сеть надо поменять в settings -> proxy -> socks5 host с 127.0.0.1 на 0.0.0.0.

Пребилды как всегда [тут], а щитпостильня с обсуждением багов и feature-request'ов - [здесь] 😋
Please open Telegram to view this post
VIEW IN TELEGRAM
8🔥528💅3🙏2
Кажется цивилизованному миру все же нужен не дискорд по паспорту, а ЛЛМки по справке от психиатра
1😁102💊338🔥2