Раздвојени префил и декодирање
Одвајање префила и декодирања преко уређаја убрзава ЛЛМ инференцију и побољшава перформансе
Да би се генерисали излазни токени из улазног упита, ЛЛМ инференција је подељена у две фазе: префил и декодирање. Префил се извршава над улазним токенима, попуњавајући КВ кешеве, при чему се затим улази у фазу декодирања која генерише токене један по један. Док један корак декодирања обично траје десетине милисекунди, префил траје знатно дуже. Ако се покрену на истим уређајима, мешање префила са декодирањем деградира перформансе декодирања. У овом чланку истражујемо устаљено рјешење у облику раздвојеног префила и декодирања, покрећући их на одвојеним уређајима како би се максимизирали и пропусни опсег префила и кашњења декодирања.
Перформансе префила у односу на декодирање
У типичном ЛЛМ сервисирајућем систему, распоређивач серија бира захтеве за обраду у сваком кораку извршавања модела. При раду на једном уређају или чвору, захтеви за префил и декодирање се групишу заједно. Цена пажње, која се акумулира дуж дужине секвенце, расте и за префил и за декодирање, пропорционално дужини уноса у КВ кешу (kv_len). Захтеви за декодирање обично прослеђују један токен (qo_len=1), уз минималну цену кроз друге слојеве који раде независно на токенима секвенце. Захтеви за префил прослеђују хиљаде или десетине хиљада токена уз значајну цену кроз густе слојеве (велики qo_len).
На кашњење пролаза напред снажније утиче број независних токена пропуштених кроз густе слојеве (qo_len) него број токена преузетих из КВ кеша током пажње (kv_len). Пажња може да паралелизује и у односу на број захтева и kv_len пропорционално дужинама секвенци, постижући добру искоришћеност. Префил је везан за рачунање: како је qo_len висок, ГЕММ језгра могу да доделе довољно блокова дуж M димензије да у потпуности искористе рачунске могућности модерних ГПУ-ова. Декодирање је везано за меморију: због обично ниских величина серија, број уноса дуж M је обично мали, довољан за само један блок. Док Split-K ГЕММ језгра могу побољшати СМ искоришћеност за мале величине серија токена, кешеви и јединице за множење матрица обично остају недовољно искоришћени.
Када се помешају заједно, серије које садрже захтеве за префил изазивају већа кашњења кроз пролаз напред, што негативно утиче на пропусни опсег декодирања целе инстанце. Док мешање захтева за префил са захтевима за декодирање или коришћење сецканог префила може незнатно побољшати перформансе декодирања, тешко је одржати довољан пропусни опсег префила за обраду довољно захтева на инстанци да се максимизира пропусни опсег декодирања. У случају великих модела, са типичним излазним дужинама, да би се одржала велика величина серије за декодирање, префил се мора изводити довољно често да значајно деградира просечно кашњење и изазива застоје у излазу.
Ови проблеми се могу решити коришћењем одвојеног скупа чворова за извршавање префила и декодирања. Удруживањем чвора за префил са више чворова за декодирање, довољно захтева се може распоредити за префил како би се максимизирао пропусни опсег и одржао довољно велики број истовремених захтева на чворовима декодера како би се такође максимизирао пропусни опсег декодирања. Чворови за префил попуњавају КВ кешеве, који се затим преносе на чворове декодера. Пошто декодери више не морају да праве паузе за префил, кашњења постају много детерминистичкија, јер је укупан утицај растућег kv_len активних захтева много мање изражен. Цена се плаћа у повећању времена до првог токена (ТТФТ), јер пренос КВ кешева преко мреже може потрајати десетине до стотине милисекунди.
КВ гласник
У Перplexity-ју, наша имплементација за раздвојени префил и декодирање изграђена је око КВ гласника који комуницира са ЛЛМ мотором како би оркестрирао преносе КВ кеша са чворова за префил на чворове декодера преко мреже. На страни префила, гласник прихвата захтеве од чворова декодера, прослеђујући их распоређивачу серија и пратећи извршавање пролаза напред како би отпремио КВ кешеве са што мањим кашњењем. На страни декодера, након што су додељене странице које се не могу избацити, гласник блокира захтев да буде распоређен за декодирање све док не буде обавештен о завршетку преноса КВ кеша и контекста декодера.
Раздвајање префила захтева везе високе пропусне моћи и малог кашњења, стога је наша имплементација прилагођена за РДМА, подржавајући и ЕФА и ЦоннектКС мрежне интерфејс контролере (НИЦ). КВ гласник је изграђен на libfabric-у, користећи наше fabric-lib омотаче да пружи апстракције вишег нивоа са ниским кашњењем над примитивима удаљеног директног приступа меморији (РДМА), имплементирајући ефикасне преносе страница и метаподатака, заједно са сигналізацијом малог кашњења. У позадини, fabric-lib координира ГПУ и његове директно повезане НИЦ-ове за копирање података са чвора за префил на чвор декодера.
По пријему, чвор за префил додељује одговарајући скуп изворних КВ страница и заказује захтев за префил користећи свој локални мотор. Да би се минимизирало кашњење, преноси не чекају пролаз напред: уместо тога, копирање КВ страница се покреће чим модел заврши додавање уноса КВ кеша у КВ кеш за појединачне слојеве. Пошто захтеви за префил могу бити сецкани, распоређивач серија обавештава КВ гласник о тренутно распоређеним парчићима пре извршавања. Да би подржао ЦУДА графиконе уз могућност праћења слојева, гласник чува посвећени тред који прати бројач увећан након излазне пројекције пажње. Бројач се одржава само на главном чвору у дељеном окружењу: иако су уноси КВ кеша важећи након додавања и прије пажње, излазна пројекција се редукује кроз рангове, имплицитно их синхронизујући. Када се уочи промена у бројачу, гласник се обавештава и позива fabric-lib да покрене пренос слоја.
Након што је пренос последњег парчића завршен, сви додатни метаподаци се такође копирају: шпекулативно декодирање или МТП захтевају да се логити и скривена стања преместе на декодер. Ова копирања се такође изводе преко РДМА, до и од унапред додељених бафера.
Након завршетка свих преосталих преноса последњег парчића, чвор за префил поништава доделу КВ страница и завршава захтев. Чвор декодера није експлицитно обавештен: уместо тога, користи тренутне бројаче за праћење броја завршених операција. Број РДМА операција на страни префила је пропорционалан броју пренетих страница. По завршетку познатог броја копија страница и контекста, fabric-lib позива КВ гласник да укаже да је захтев спреман за декодирање. Гласник поништава доделу било ког контекста и предаје захтев ЛЛМ мотору.
Преноси дељеног КВ кеша
Ако префил и декодер се ослањају на паралелизам тензора (ТП) и деле или реплицирају КВ кешеве идентично, један мотор за пренос координира више уређаја за слање и пријем страница свих реплика. Да би се могао користити један гласник и мотор за пренос упркос чињеници да је извршилац модела реплициран на више уређаја и процеса, cuMem и cuMemImportFromShareableHandle се користе за додељивање меморије уређаја која подржава КВ кешеве и њено мапирање у главни процес. Мотор за пренос инспектира топологију чвора како би пронашао НИЦ-ове и ЦПУ-ове у најближем НУМА чвору који ће се користити за преносе сваког од делова КВ кеша.
Ако се извор и одредиште деле идентично, преноси су тривијални јер постоји пресликавање један на један са уређаја и страница извора и одредишта. У овој ситуацији, дељење имплицитно помага кашњењима преноса: коришћењем више ГПУ-ова, може се запослити више повезаних НИЦ-ова, достижући ближе пуној искоришћености пропусног опсега. Међутим, ако постоји неподударање, мотор за пренос мора да подели или реконструише странице у зависности од односа између изворних и одредишних делова.
Ако префил дели КВ кеш на више уређаја, пуне странице се реконструишу на декодеру слањем одговарајућих половина са уређаја за префил. Ако декодер има више делова, он прима странице из више извора. Декодер мора да зна шему дељења префила како би могао да израчуна број РДМА уписа које се очекује да прими. Ако је репликација укључена, префил групе уређаје у скупове реплика који реплицирају пун КВ кеш унутар себе. Одредишни скупови реплика су насумично додељени једном од изворних скупова како би се користили сви доступни уређаји за покретање РДМА уписа.
Дељени преноси захтевају мало дотеривање КВ кешева. Подразумевано, ФласхИнфер се ослања на NHD распоред, који уређује токене унутар странице унутар глава. Пошто су кешеви највјероватније подељени дуж броја глава пажње, ово ствара дисконтинуитет унутар главе. РДМА преноси не подржавају имплицитно уписе са корацима, захтевајући једну операцију по глави за извођење преноса. Уместо тога, да бисмо смањили број интеракција са libfabric, организујемо КВ кешеве користећи HND распоред који ставља димензију главе испред броја токена. Ово обезбеђује континуитет, омогућавајући да се страница копира једним уписом.
Шпекулативно декодирање
Speculative decoding захтева мале измјене у раздвојеном prefill-decode процесу. У нашој имплементацији, чворовима за prefilling није дозвољено да узоркују токене. Пошто модели Sonar компаније Perplexity подржавају структурирани излаз, не желимо да преузмемо сложеност синхронизације имплементација процесора шеме између чворова за prefilling и декодера. У механизмима MTP и speculative decoding, предунашање draft модела до посљедњег токена укључује узорковање токена из циљног модела.
Да би се заобишли ови проблеми, префил не укључује последњи токен улазне секвенце. Уместо тога, скривена стања или логити из префила који претходе последњем токену се преносе и третирају се као токен декодирања у следећем кораку на декодеру. Иако ово незнатно повећава кашњења, пошто се пун корак декодирања мора извршити након префила да би се издао први токен, сложеност имплементације је знатно смањена.
Раздвојена имплементација
Распоредили смо или експериментисали са више раздвојених конфигурација са различитим моделима, како бисмо подржали или производни саобраћај или интерна радна оптерећења евалуације. На основу величине и механизма пажње модела, изабрали смо одговарајуће шеме дељења за чворове префилера и декодера како бисмо најбоље искористили ГПУ-ове.
DeepSeek-R1
Са DeepSeek-ом, разматрали смо и имплементације са паралелизмом тензора (ТП) и са паралелизмом података (ДП). Као што је разматрано у претходним блог објавама, ТП имплементације обезбеђују боље кашњење уз цену нижег пропусног опсега, захтевајући више ГПУ-ова за опслуживање тешког саобраћаја. ДП имплементације се скалирају много боље са оптерећењем, међутим њихов вршни пропусни опсег је нижи због цене комуникације између уређаја или између чворова.
DeepSeek се ослања на мултихед латентну пажњу, компримујући КВ кешеве. Пошто су све КВ главе компримоване у један латентни вектор, ТП не може да подели КВ кешеве, јер уместо тога мора да реплицира латентне векторе на свим ранговима. Дељење се дешава након декомпресије, пошто сваки ранг може да извуче различите главе из исте латентне репрезентације. Последично, сви делови КВ кеша су идентични широм делова префилера и декодера.
Са интранодним ТП подешавањем, и префилери и декодери су идентично подељени. Преноси се отпремају са свих рангова како би се у потпуности искористили сви доступни НИЦ-ови. Међутим, са ДП имплементацијом, где је величина ТП ранга мања или је сваки ДП ранг додељен једном ГПУ-у, било који уређај префилера који држи реплицирану копију КВ кеша може га отпремити. Да бисмо балансирали захтеве преко свих доступних НИЦ-ова, насумично бирамо ГПУ и НИЦ за слање КВ кеша са префилера на декодер.
Са мешовитим префилом и декодирањем, наша R1 имплементација се мучила да доследно премаши 50 TPS због честих прекида префила у реду величина од стотина милисекунди. Насупрот томе, одвајањем префила, имали смо казну од око 100ms за TTFT за сваки захтев, али је један чвор префилера могао да одржи конзистентне величине серија на 3 чвора декодера, испоручујући пропусни опсег већи од 90 TPS уз руковање оптерећењем од око 1 QPS по чвору декодера. Са имплементацијама паралелним са подацима, TPS је био незнатно нижи на око 50, међутим инстанце су могле да поднесу оптерећење од 1 QPS по рангу, са 8 рангова на једном чвору.
Qwen3-Coder
Овај модел од 480B користи пажњу са груписаним упитима (GQA), тако да се пажња може лако поделити и може имати користи од паралелизма тензора без жртвовања меморије за КВ кешеве. Сходно томе, могли смо да поделимо модел на 8 ГПУ-ова и за префил и за декодирање, упарујући око 3 чвора декодера са једним чвором префилера. Пошто је пажња подељена, ослањамо се на HND распоред КВ кеша да бисмо поделили КВ кешеве префилера и декодера, упарујући рангове префилера са ранговима декодера и у потпуности користећи све НИЦ-ове за паралелни пренос делова.
Цитирање
@online{perplexity2025DisaggregatedPrefillAndDecode,
author = {{Perplexity} and Nandor Licker and Lequn Chen and Kevin Hu and Erin McKnight and Phi Hoang},
title = {{Disaggregated prefill and decode}},
date = {2025-08-01},
url = {https://perplexity.coolkeypoint.com/hub/blog/disaggregated-prefill-and-decode}
}