Sakhanda Wire
NVDA $230.86 +1.09% MSFT $512.80 -0.02% GOOGL $338.24 -1.70% META $725.93 +0.10% AMZN $248.23 -0.37%
← Към новините

Perplexity представя подробно своя GPU стек за ембединг: как Ivy, Tulip и ROSE обслужват pplx-embed

Качеството на извличане в продукт за AI търсене се определя от две неща: колко добър е моделът за вграждания и колко евтино можете да го изпълнявате върху индекс. Тази седмица екипът по инженеринг на Perplexity публикува Бързи вграждания върху GPU — поглед отвътре към второто, а именно инфраструктурата за обслужване зад pplx-embed и моделите за класиране, използвани в Perplexity Search, Computer и API Platform.

Екипът на Perplexity посочва, че инференцията на вграждания върху GPU до голяма степен се е уеднаквила между различните енджини при утвърдения хардуер Hopper и Blackwell. Подобренията са в средата за изпълнение и обвивката около модела: управление на CUDA графи, абстракция за асинхронно проследяване на резултатите и път за заявки, изграден на Rust.

Два модела на трафика, един енджин

Perplexity разглежда обслужването на вграждания като два типа работни натоварвания. Пакетното вграждане се извършва при изграждане или повторно индексиране на векторната база данни, където пропускателната способност минимизира разходите. Онлайн вграждането се извършва по време на заявката, когато кратката заявка трябва да бъде вградена бързо. Класирането е по средата: след векторното търсене големи пакети документи се класират, като се балансират и двата фактора.

Ключовото решение е, че Perplexity не е изградила отделен енджин за вграждания. Тъй като моделите за вграждания са малки Transformer модели, пакетното вграждане прилича на изчислително ограничен prefill, а онлайн вграждането, често състоящо се от няколко токена, прилича на декодиране, ограничено от паметта. Затова изследователският екип използва повторно ядрата за prefill и decode от своя LLM стек.

Ivy, Tulip и ROSE

Три услуги обработват една заявка:

  • Ivy е Rust HTTP шлюз. Той извършва работата от страна на CPU — анализира JSON, извършва токенизация, прилага шаблони към входа, разделя пакетите — и превежда заявките в персонализиран gRPC протокол. Освен това разделя заявките с големи пакети на части и разпределя натоварването между репликите, което коригира дисбаланса в натоварването, възникващ при различни по размер производствени полезни товари.
  • Tulip е интерфейсът на сървъра за инференция: gRPC сървър, изграден с Rust, tokio и tonic, който обработва планирането и пакетното групиране, преди да предаде задачите към енджина.
  • ROSE (Runtime-Optimized Serving Engine) реализира инференцията на модела. Той е предимно на Python, предоставя ядра, слоеве и дефиниции на модели, управлява CUDA графи и предоставя функция step() на Tulip.

Защо планировчикът е умишлено прост

Tulip избира последователностите по реда на постъпване, докато заявките се натрупват. Тази простота е оправдана от едно измерване: при малките модели за вграждания и дължините на последователностите, които Perplexity обслужва, линейната цена на плътните слоеве доминира над квадратичната цена на вниманието. Следователно латентността е приблизително пропорционална на броя токени, а не на броя последователности. След като пакетът насити GPU, около 512 токена при модел с под един милиард параметъра, добавянето на още последователности не подобрява ефективността.

CUDA графи и LazyTensors

При малки пакети стартирането на ядра от страна на CPU може да надхвърли времето за изпълнение върху GPU. Perplexity изгражда CUDA графи на целия модел за всички модели за вграждания, като улавя всяко стартиране в едно извикване към драйвера. Тъй като моделите за вграждания са малки, точката, в която работата на GPU надхвърля разходите за стартиране, настъпва при пакети от хиляди токени и десетки последователности. Някои реализации на вниманието блокират графите на целия модел, като зависят от динамични входове от страна на хоста; Perplexity внесе промени upstream в FlashInfer, за да позволи улавянето.

Графите трябва да бъдат улавяни за всяка конфигурация, затова броят токени се допълва до кофи, кратни на 64 или 256. Това все пак води до хиляди графи и няколко минути улавяне за всеки модел. Решението е мързеливо улавяне: всяка конфигурация получава нетърпеливо изпълнение за загряване, след което задейства улавяне и повторно възпроизвеждане при второто си използване. Това увеличава латентността p99 при стартиране, но разпределя минутите нетърпелива работа в продължение на часове.

Втората част е LazyTensor, който проследява заключен за страници буфер на хоста, както и cudaMemcpyAsync и CUDA събитие. Вместо step() да блокира върху устройството, той връща LazyTensor, позволявайки на асинхронна Rust задача да изчака пакет N, докато CPU поставя в опашката пакет N+1.

Send a request</button></div> </div> </div> <div class="pe-panel" id="peP1"> <div class="pe-card"> <div class="pe-txt">При малки пакети стартирането на ядра от страна на CPU може да надхвърли работата върху GPU. <b>CUDA графът</b> на целия модел улавя всяко стартиране в едно извикване към драйвера, така че CPU се освобождава, за да постави в опашката следващия пакет. Превключвайте между двата режима.</div> <div class="pe-ctl" style="margin:0 0 12px"> <button class="pe-btn ghost on" id="peEager">Нетърпеливи стартирания</button> <button class="pe-btn ghost" id="peGraph">CUDA граф</button> </div> <div class="pe-lane"><div class="pe-lbl">Хост / CPU</div><div class="pe-track" id="peCpuT"></div></div> <div class="pe-lane"><div class="pe-lbl">Устройство / GPU</div><div class="pe-track" id="peGpuT"></div></div> <div class="pe-stats"> <div class="pe-stat"><div class="v" id="peLaunches">—</div><div class="k">Извиквания към драйвера</div></div> <div class="pe-stat"><div class="v" id="peGap">—</div><div class="k">Празни интервали на GPU</div></div> </div> <div class="pe-txt" style="margin:12px 0 0;font-size:11.5px;color:#6E8285">Схематично представяне. Широчините на блоковете илюстрират модела на разходите за стартиране, описан в публикацията, а не измерени времена.</div> </div> </div> <div class="pe-panel" id="peP2"> <div class="pe-card"> <div class="pe-txt">Обичайното четене на резултатите обратно принуждава синхронизация с хоста. <b>LazyTensor</b> проследява заключен за страници буфер на хоста, както и асинхронно копиране от устройството към хоста и CUDA събитие, така че Tulip може да блокира върху пакет N, докато CPU вече подготвя пакет N+1.</div> <div class="pe-lane"><div class="pe-lbl">CPU — подготовка / синхронизация</div><div class="pe-track" id="peLzC"></div></div> <div class="pe-lane"><div class="pe-lbl">GPU — пряко изпълнение</div><div class="pe-track" id="peLzG"></div></div> <div class="pe-ctl"> <button class="pe-btn" id="peLzRun"> Стартирай 3 пакета</button> <button class="pe-btn ghost on" id="peLzOn">С припокриване</button> <button class="pe-btn ghost" id="peLzOff">Блокиращо</button> </div> <div class="pe-note" style="margin-top:12px" id="peLzNote">С припокриване: докато GPU обработва пакет N, CPU вече токенизира и пакетира пакет N+1.</div> </div> </div> <div class="pe-panel" id="peP3"> <div class="pe-card"> <div class="pe-txt">При малки модели за вграждания и тези дължини на последователностите линейната цена на плътните слоеве доминира над квадратичната цена на вниманието, така че латентността следва <b>броя токени, а не броя последователности</b>. След приблизително <b>512 токена</b> при модел с под 1B параметъра GPU е наситен и добавянето на още последователности вече не помага.</div> <div class="pe-lbl" style="margin-top:6px">Токени в пакета: <span id="peTokV" style="color:#3FB6C4">512</span></div> <input type="range" id="peTok" min="32" max="4096" step="32" value="512"> <div class="pe-lbl">Използване на GPU</div> <div class="pe-bar"><div class="pe-fill" id="peUtil"></div></div> <div class="pe-stats"> <div class="pe-stat"><div class="v" id="peUtilV">—</div><div class="k">Насищане</div></div> <div class="pe-stat"><div class="v" id="peState">—</div><div class="k">Режим</div></div> </div> <div class="pe-txt" style="margin:12px 0 0;font-size:11.5px;color:#6E8285">Илюстративна крива. Точката на насищане при около 512 токена е посочената в публикацията стойност; формата между точките е ориентировъчна, а не резултат от бенчмарк.</div> </div> </div> <div class="pe-foot"> <span>Източник: Perplexity Engineering, „Fast Embeddings on GPUs“ (4 септември 2026 г.)</span> <span><a href="https://www.marktechpost.com">Изградено от Marktechpost</a></span> </div> <script> (function(){ var R=document.getElementById('pplxEmbedExplainer'); var NOTES=[ '<b>Ivy</b> — анализира JSON, токенизира с вътрешния unigram токенизатор, прилага шаблони към входа и разделя големите пакети, след което превежда към персонализиран gRPC протокол. Също така балансира натоварването на частите между репликите.', '<b>Tulip</b> — Rust gRPC сървър върху tokio и tonic. Заявките се натрупват, докато той ги изпраща или изчаква; последователностите се избират по реда на постъпване и се пакетират в пакет за ускорителя.', '<b>ROSE</b> — Runtime-Optimized Serving Engine. Ядра и слоеве, дефинирани на Python, управление на CUDA графи и функция step(), която връща дескриптор към изчислението върху GPU. За вграждания не се заделя KV кеш.' ]; function q(s){return R.querySelector(s)} function qa(s){return R.querySelectorAll(s)} /* раздели */ qa('.pe-tab').forEach(function(t){t.addEventListener('click',function(){ qa('.pe-tab').forEach(function(x){x.classList.remove('on')});t.classList.add('on'); qa('.pe-panel').forEach(function(p,i){p.classList.toggle('on',i==+t.dataset.p)});});}); /* 1 поток */ var note=q('#peNote'),dot=q('#peDot'); function sel(i){qa('.pe-node').forEach(function(n,k){n.classList.toggle('hot',k==i)});note.innerHTML=NOTES[i];} qa('.pe-node').forEach(function(n){n.addEventListener('click',function(){sel(+n.dataset.i)})}); sel(0); var busy=false; q('#peGo').addEventListener('click',function(){ if(busy)return;busy=true;var steps=[[0,'3%'],[1,'40%'],[2,'76%']],k=0; dot.style.transition='none';dot.style.left='3%';dot.style.opacity='1'; sel(0); var iv=setInterval(function(){k++;if(k>2){clearInterval(iv);dot.style.opacity='0';busy=false;return;} dot.style.transition='left .8s cubic-bezier(.4,0,.2,1)';dot.style.left=steps[k][1];sel(k);},900); }); /* 2 CUDA графи */ var cpuT=q('#peCpuT'),gpuT=q('#peGpuT'),mode='eager'; function blk(p,l,w,cls,txt){var d=document.createElement('div');d.className='pe-blk '+cls;d.style.left=l+'%';d.style.width=w+'%';d.textContent=txt||'';p.appendChild(d);} function drawG(){ cpuT.innerHTML='';gpuT.innerHTML=''; if(mode=='eager'){ for(var i=0;i<6;i++){blk(cpuT,1+i*16.4,7,'pe-cpu','стартиране');blk(gpuT,8.4+i*16.4,7.6,'pe-gpu','ядро');if(i<5)blk(gpuT,16+i*16.4,7.2,'pe-idle','изчакване');} q('#peLaunches').textContent='6';q('#peGap').textContent='5'; }else{ blk(cpuT,1,12,'pe-cpu','стартиране на графа');blk(cpuT,15,26,'pe-cpu','подготовка на следващия пакет'); blk(gpuT,13.5,84,'pe-gpu','6 ядра — едно възпроизвеждане, без пропуски'); q('#peLaunches').textContent='1';q('#peGap').textContent='0'; }} q('#peEager').addEventListener('click',function(){mode='eager';q('#peEager').classList.add('on');q('#peGraph').classList.remove('on');drawG();}); q('#peGraph').addEventListener('click',function(){mode='graph';q('#peGraph').classList.add('on');q('#peEager').classList.remove('on');drawG();}); drawG(); /* 3 LazyTensor */ var lzC=q('#peLzC'),lzG=q('#peLzG'),lzMode='on',lzBusy=false,lzNote=q('#peLzNote'); function drawLz(){ lzC.innerHTML='';lzG.innerHTML=''; for(var i=0;i<3;i++){ if(lzMode=='on'){blk(lzC,2+i*32,14,'pe-cpu','подготовка '+(i+1));blk(lzG,17+i*32,26,'pe-gpu','пакет '+(i+1));} else{blk(lzC,2+i*32,12,'pe-cpu','подготовка '+(i+1));blk(lzC,15+i*32,16,'pe-idle','изчакване');blk(lzG,15+i*32,15,'pe-gpu','пакет '+(i+1));} }} function lzRun(){ if(lzBusy)return;lzBusy=true;drawLz(); var bs=lzG.querySelectorAll('.pe-blk'),cs=lzC.querySelectorAll('.pe-blk'); [].forEach.call(bs,function(b){b.style.opacity='.15'});[].forEach.call(cs,function(b){b.style.opacity='.15'}); var all=[].concat([].slice.call(cs),[].slice.call(bs)),j=0; var iv=setInterval(function(){if(j>=all.length){clearInterval(iv);lzBusy=false;return;}all[j].style.opacity='1';j++;},220); } q('#peLzRun').addEventListener('click',lzRun); q('#peLzOn').addEventListener('click',function(){lzMode='on';q('#peLzOn').classList.add('on');q('#peLzOff').classList.remove('on');lzNote.innerHTML='С припокриване: докато GPU обработва пакет N, CPU вече токенизира и пакетира пакет N+1.';drawLz();}); q('#peLzOff').addEventListener('click',function(){lzMode='off';q('#peLzOff').classList.add('on');q('#peLzOn').classList.remove('on');lzNote.innerHTML='Блокиращо: всяко извикване на step() изчаква устройството, така че CPU остава в бездействие, а GPU стартира със закъснение.';drawLz();}); drawLz(); /* 4 форма на пакета */ var tok=q('#peTok'); function drawT(){ var v=+tok.value;q('#peTokV').textContent=v; var u=Math.min(100,Math.round(100*(1-Math.exp(-v/230)))); q('#peUtil').style.width=u+'%';q('#peUtilV').textContent=u+'%'; q('#peState').textContent=v<512?'Недостатъчно запълнен':(v<1200?'Наситен':'Ограничен от пропускателната способност'); q('#peState').style.color=v<512?'#C7A24E':'#3FB6C4'; } tok.addEventListener('input',drawT);drawT(); })(); </script> </div> <script> (function(){function h(){var e=document.getElementById('pplxEmbedExplainer');if(!e)return;parent.postMessage({pplxEmbedH:e.offsetHeight+40},'*');} window.addEventListener('load',h);setTimeout(h,300);setTimeout(h,1200); document.addEventListener('click',function(){setTimeout(h,250)}); document.addEventListener('input',function(){setTimeout(h,120)}); if(window.ResizeObserver){var e=document.getElementById('pplxEmbedExplainer');if(e)new ResizeObserver(h).observe(e);}})(); </script> </body></html>">

Ядрата все още са важни

ROSE поддържа няколко бекенда за внимание при неравномерни входове: FlashInfer 2, FlashInfer 3 и FlashAttention 4. Екипът на Perplexity съобщава, че FlashAttention 4 обикновено е по-бърз, но FlashInfer 3 го превъзхожда при модели, базирани на Qwen, при много дълги последователности, затова изборът на бекенд се прави според конкретния случай. Забележително е, че при обслужване на модел за вграждания ROSE не създава KV кеш и използва варианти на вниманието за неравномерни входове, за да избегне допълването.

Бенчмаркове

Perplexity прави бенчмарк спрямо vLLM v0.22.0 в BF16, използвайки реални тегла и входове, извлечени от оценяването, като стартиранията за загряване потвърждават, че отклонението в косинусната близост е в рамките на 0,1%. Представени са четири набора: вграждания с ниска латентност (пакет 1; 128/512/4096 токена), класиране с ниска латентност (пакети 5/25/50 при 512 токена), вграждания с висока пропускателна способност (пакет 100, четири едновременни процеса) и вграждания с висока конкурентност (от 1 до 16 едновременни заявки, включително токенизацията на Ivy и мрежовите разходи).

Основни изводи

  • Стекът за вграждания на Perplexity използва повторно ядрата за prefill/decode на своя LLM, вместо да изпълнява отделен енджин.
  • Латентността следва броя токени, а не броя последователности; около 512 токена насищат модел с под 1B параметъра.
  • CUDA графите на целия модел, комбинирани с мързеливо улавяне, намаляват разходите за стартиране, без да е необходимо начално изпълнение от няколко минути.
  • LazyTensor припокрива подготовката на пакета от CPU с текущата работа върху GPU, вместо да блокира при синхронизация.
  • Ivy, Tulip и ROSE са вътрешни компоненти; pplx-embed е достъпен чрез API за вграждания на Perplexity.

Разгледайте техническите подробности. Също така можете да ни последвате в Twitter и не забравяйте да се присъедините към нашия ML SubReddit с над 150 хил. членове и да се абонирате за нашия бюлетин. Чакайте! В Telegram ли сте? Вече можете да се присъедините към нас и в Telegram.

Имате нужда от партньорство с нас за популяризиране на вашето GitHub хранилище ИЛИ страница в Hugging Face ИЛИ продуктова версия ИЛИ уебинар и т.н.? Свържете се с нас

Преведено автоматично от английски. Оригиналната статия е на връзката по-долу.

Първоначално публикувано от MarkTechPost на

Прочетете оригинала в MarkTechPost ↗

Текстът и изображенията са собственост на MarkTechPost и са възпроизведени тук с посочване на авторството и връзка към оригиналната публикация.

← Към новините

Още новини

Всички последни новини