GPU-лардағы жылдам ендірулер
Жылдам әрі дәл іздеу Іздеу және Computer-ден бастап біздің API Platform-ға дейін барлық Perplexity үшін өте маңызды. Сахна артындағы ауыр жұмысты жүйелерімізге берілген сұрау үшін ең сәйкес нәтижелерді анықтауға көмектесетін ендіру және рангтеу модельдері атқарады. Біз заманауи жағдайға қол жеткіземіз
Жылдам әрі дәл іздеу Іздеу және Computer-ден бастап біздің API Platform-ға дейін барлық Perplexity үшін өте маңызды. Сахна артындағы ауыр жұмысты жүйелерімізге берілген сұрау үшін ең сәйкес нәтижелерді анықтауға көмектесетін ендіру және рангтеу модельдері атқарады. Біз pplx-embed сияқты өз модельдерімізді оқыту және қызмет көрсету арқылы заманауи сапа мен кідіріске қол жеткіземіз.
Бұл мақалада осы арнайы модельдер класы үшін Perplexity қызмет көрсету инфраструктурасының көрінісі ұсынылған. Біз AI-негізіндегі іздеудің inference қажеттіліктерін тиімді шешу техникаларын талқылаймыз, бұл модельдерді жылдам прототиптеу мен бағалауға мүмкіндік береді, сонымен бірге біздің экзабайттық ауқымдағы іздеу индексімізді жұмыс істетеді. Бұл техникалар бірлесіп іздеу сапасы мен тиімділігінің Парето шекарасын кеңейтеді, бұл бізге ең төменгі құн мен кідіріспен агенттер мен пайдаланушыларға ең жақсы нәтижелерді ұсынуға мүмкіндік береді.
Іздеуге арналған ендірулер
Әдеттегі іздеу орнатуында индекстелген құжаттар ендіру моделі арқылы жоғары өлшемді векторлық кеңістікке картаға түсіріледі және векторлық дерекқорда сақталады. Дәл сол модельді пайдаланып сұрауды ендіру арқылы ұқсас құжаттар сұрау векторына ең жақын векторларды табу арқылы табылуы мүмкін. Бұл inference қозғалтқышы қызмет көрсететін екі түрлі трафик үлгісін тудырады:
- Пакеттік ендіру: дерекқорды құру, кеңейту немесе қайта индекстеу кезінде, шығындарды барынша азайту үшін өткізу қабілетін барынша арттыру мақсатында көлемді құжаттар векторлық кеңістікке ендірілуі тиіс.
Векторлық іздеуден кейін, өткізу қабілеті мен кідіріс арасындағы тепе-теңдікті сақтай отырып, құжаттардың үлкен пакеттеріне бағалау жасалуы тиіс.
- Онлайн ендіру: дерекқорға сұрау жасаған кезде, іздеу үшін қысқа сұрау ендірілуі тиіс, бұл кідірісті барынша азайтады.
Біз пайдалану жағдайлары бойынша мүмкіндігінше көп ортақ компоненттерді пайдалану үшін inference инфраструктурамызды құрдық. Біз әдетте ендірулерді шығару үшін кішігірім Transformer модельдерін пайдаланатындықтан, іске асырудың негізгі бөлігін LLM inference кодымен бөлісеміз: пакеттік ендірулер есептеулермен шектелген prefill кезеңіне ұқсайды, ал көбінесе бірнеше токенде жұмыс істейтін онлайн ендірулер есептеу жағынан жадпен шектелген decode кезеңіне ұқсайды. Сондықтан біз ендіру модельдеріне қызмет көрсету үшін оңтайландырылған prefill және decode kernel-дерін қайта пайдаланамыз. Нәтижесінде біз онлайн ендіру жұмыс жүктемелері үшін төмен кідірісті сақтай отырып, қосымша инженерлік жұмысты ең аз деңгейге дейін азайта отырып, жаппай пакеттік inference өткізу қабілетіне қол жеткізе аламыз.
Tulip-тер, Rose-тар және кейбір Ivy
Біз inference мүмкіндігін стандартталған API интерфейстері арқылы, ішкі жағынан да, API Platform арқылы сырттай да ұсынамыз. Сахна артында ендіру сұрауын өңдеуге бірнеше қызмет қатысады:
- Ivy — бұл Perplexity қызметтері шақыратын Rust HTTP шлюзі.
Ол JSON талдау, токенизация, енгізу шаблондарын жасау және пакеттерді бөлу сияқты сұраулар үшін CPU жағындағы жұмысты өңдейді, сұрауларды төменгі ағынды серверлер үшін реттелетін gRPC протоколына аударады. Бұл бөлу бізге ауырлау inference мысалдарын түртпей-ақ токенизация мен енгізу пішімін жасау төңірегінде белгілі бір параметрлерді конфигурациялауға мүмкіндік береді.
- Tulip — бұл inference серверінің интерфейсі.
Бұл Rust, tokio және `tonic` арқылы іске асырылған gRPC сервері. Tulip gRPC inference сұрауларын қабылдайды, жоспарлау мен пакеттеуді басқарады. Содан кейін ол пакеттерді ROSE қозғалтқышына жібереді, аяқталған жауаптарды клиенттерге қайтарады.
- **ROSE** (Runtime-Optimized Serving Engine) модельді inference жұмысын атқарады.
Ол негізінен Python тілінде анықталған және модельдердің кең ауқымы үшін kernel-дерді, қабаттарды және анықтамаларды ұсынады. ROSE модельдер арқылы алға бағытталған өтулерді жүзеге асырады, сонымен қатар ендірулер үшін мамандандырылған CUDA графы басқаруын қамтамасыз етеді. Ол пакетті қабылдайтын және үдеткіште орындайтын есептеу сілтемесін қайтаратын step() функциясы арқылы Tulip жүйесімен байланысады.

Kernel-ден тыс назар аудару
Transformer негізіндегі модельдер де, түпкі Hopper/Blackwell архитектуралары да жетілген технологиялар болып табылады, сондықтан GPU жағында inference ендіру әр түрлі inference қозғалтқыштарында негізінен оңтайлы іске асырылуға жетті. Соған қарамастан, біз модельдерді клиентке ұшынан ұшына дейін шығаратын орындалу уақыттары мен жабдықтарда қосымша жақсарту мүмкіндіктерін таптық. атап айтқанда, біз CUDA графтарын мұқият басқару арқылы және жергілікті Rust қозғалтқышында GPU жағындағы нәтиже қадағалау үшін асинхронды LazyTensor абстракциясын құру арқылы кідірістерді жақсарта алатынымызды анықтадық. Біз бұл мүмкіндіктерді Tulip жүйесінде іске асырдық, осылайша ол ROSE модельдерін іске асыру интерфейсімен тиімді жұмыс істей алады.
Tulip
Біз Tulip интерфейсін модельге қызмет көрсетудің үстінде мүмкіндігінше жеңіл етіп жобаладық. Ол кіріс сұрауларды Tokio асинхронды тапсырмаларында өңдейді, ол қадағалайтын және үдеткішке жіберу үшін пакеттерді жоспарлайтын сұраулар пулын сақтайды. Tulip жүйесіндегі жоспарлау механизмі өте қарапайым: Tulip жұмысты жіберіп жатқанда немесе нәтижелерді күтіп жатқанда сұраулар жиналады. Жиналған сұраулардан модель арқылы өту үшін тізбектер бірінші келгенге, бірінші қызмет көрсетіледі қағидасы бойынша таңдалады.
Қарапайым жоспарлау механизмі модель өнімділігі туралы бақылау арқылы ынталандырылады. Кішкентай ендіру модельдері үшін, біз қызмет көрсететін тізбек ұзындықтарында, тығыз қабаттардың сызықтық құны назар аударудың квадраттық құнынан басым екенін байқадық. Осылайша, кідіріс тізбектер санына емес, негізінен токендер санына пропорционалы. Демек, пакет GPU-ды толық жүктеу үшін жеткілікті үлкен болғаннан кейін (бұл миллиардтан аз параметрлері бар модельде шамамен 512 токен), оған көбірек тізбектерді жинау тиімділікті арттырмайды.
Модельмен тиімді жұмыс істеу үшін Tulip GPU пен CPU жұмысын қабаттастыру және қолжетімді ресурстарды толық пайдалану үшін CUDA графтары мен нәтижелерді lazy қадағалауға сүйенеді.
CUDA графының басқаруы
Модельдің алға бағытталған өтуін (forward pass) орындау CPU жағындағы және GPU жағындағы жұмысты қамтиды. CPU пакеттерді жоспарлауға және тиісті параметрлері бар kernel-дерді іске қосуға жауапты, ал GPU тиісті матрицалық көбейту, назар аудару, норма немесе активация kernel-дерін орындайды. Оқыту және қайта индекстеу сияқты жоғары өткізу қабілетті жұмыс жүктемелері үшін CPU жағындағы үстеме шығындар елеусіз болып табылады, өйткені пакет өлшемдері мен GPU жағындағы кідіріс екеуі де үлкен. Дегенмен, кішірек пакет өлшемдерінде CPU жағындағы жұмыс GPU жағындағы жұмыстан асып түсуі мүмкін.

Үстеме шығындарды азайту үшін, тәуелсіз kernel-дерді іске қосудың орнына, CUDA драйверіне бір ғана шақыру арқылы алға бағытталған өтудің барлық kernel-дерін іске қосу үшін қажетті метадеректі түсіру үшін CUDA графын құруға болады. Бұл CUDA графтары түсірілуі мүмкін конфигурациялар үшін қымбат тұратын Python және PyTorch кодтарын қайта іске қосу қажеттілігін жояды.
Әрбір модель бойынша біз GPU орындалуы CPU жағындағы kernel іске қосылуынан қымбатырақ болатын ең аз токендер санын анықтайтын иілу нүктесін қадағалаймыз. Ендіру модельдері кішкентай болғандықтан, бұл иілу нүктесі мыңдаған токендер пакеттерінде және ондаған тізбектерде пайда болатынын байқаймыз. Кейбір назар аудару іске асырулары kernel іске қосылуын конфигурациялау үшін динамикалық хост жағындағы кірістерге сүйенеді, бұл толық модельдің prefill/тығыз CUDA графтарына кедергі жасайды. Біз inference қозғалтқышымызда оларды қосу үшін тиісті kernel-дерге өзгерістерді upstream еттік.
Үстеме шығындарды шешу үшін біз барлық ендіру модельдері үшін бүкіл модельдің CUDA графтарын құрамыз және CPU жұмысын GPU жұмысымен қабаттастырамыз. CUDA графтары CPU жағындағы үстеме шығындарды барынша азайтатындықтан, граф іске қосылғаннан кейін бізде келесі пакет қолжетімді болған кезде оның орындалуын бастауға және кезекке қоюға бос уақыт болады. Күтіліп жатқан пакеттің нәтижелері Rust тіліндегі асинхронды тапсырмаға алдыңғы пакеттің орындалуы аяқталғанша блокталмай тұруға мүмкіндік беретін LazyTensor көмегімен қадағаланады. CUDA графтары kernel іске қосылу құнына байланып қалмауымызды қамтамасыз ету арқылы төмен кідіріспен қызмет көрсетуге көмектеседі және CPU-ға келесі пакетте тезірек жұмыс істеуге еркіндік беретін болғандықтан, жоғары өткізу қабілетті жағдайда жақсартылған жоспарлауға ықпал етеді.

CUDA графтары әрбір әртүрлі конфигурация үшін түсірілуі тиіс, бұл ендірулер үшін тізбектер саны мен токендер санының комбинациясына шаққандағы графты білдіреді. Бұл тор кең ауқымды болғандықтан, біз токендер санын 64 немесе 256 еселіктері болып табылатын буферлерге толтырамыз. Бұл әлі де әдеттегі модель үшін түсіру үшін бірнеше минут алуы мүмкін мыңдаған графтарға әкеледі. Түсіру құны екі көзден келеді: kernel-дерді компиляциялау және оларды қажет ететін әртүрлі kernel-дер үшін буферлерді орнату үшін орындалуы тиіс eager алға бағытталған өту, содан кейін Python кодын қайта орындайтын түсіру іске қосылуы.
Біз қозғалтқыш қызмет көрсеткен кезде CUDA графтарын lazy түрде түсіру арқылы іске қосу шығындарын азайтамыз. Біз әрбір конфигурацияны қадағалаймыз және екінші рет кірген кезде графты түсіру мен қайталауды (replay) іске қоспас бұрын оның eager қыздыру жұмысынан өтуін қамтамасыз етеміз. Сол граф конфигурациясының барлық кейінгі орындалулары содан кейін CUDA графының қайталануы арқылы өтеді. Lazy графты түсіру іске қосу кезінде p99 кідірістеріне әсер етеді; дегенмен, бұл бірнеше минуттық eager жұмысын бірнеше сағатқа таратуда құнды болып табылады. Жылдамырақ іске қосу уақыттары ендіру орналастыруларын жақсырақ масштабтауға және басқаруға мүмкіндік береді.
Lazy Tensor-лар
CUDA арқылы GPU жұмысы асинхронды болып табылады. Kernel-ді асинхронды түрде іске қосу оны ағынға кезекке қоятындықтан, хост коды нәтижелі векторларды оқу үшін анық түрде синхрондалуы тиіс. Параллелизмнің жоғары дәрежесін қамтамасыз ету және құрылғыда алдыңғысының аяқталуын күтіп тұрған кезде болашақ пакеттерді бастау үшін біз мәндерді қадағалау үшін LazyTensor абстракциясына сүйенеміз.
LazyTensor беттелген-блокталған жадтағы хост буферін және құрылғыдан деректерді көшіретін оқиға арқылы cudaMemcpyAsync операциясын қадағалайды. Ол ағындағы алға бағытталған өту іске қосылғаннан кейін басталады. Көшіру операциясы ағындағы барлық алдыңғы kernel-дердің орындалуын күтуі тиіс болғандықтан, тиісті оқиға алға бағытталған өтудің аяқталуын да, CPU-да нәтиженің қолжетімді болуын да қадағалайды.

Біз GPU және CPU жұмысын қабаттастыру үшін ROSE кодтаушы қозғалтқышында LazyTensor құрылымдарын пайдаланамыз. Әрбір step() шақыруы CUDA графының іске қосылуын және оның аяқталуын күтудің орнына, step() нәтижені асинхронды түрде қадағалау үшін LazyTensor қайтарады. CUDA графтарымен үйлесімде бұл бізге төмен кідірістерге және жақсы өткізу қабілетіне қол жеткізуге көмектеседі.

ROSE
Біз бастапқыда LLM қызмет көрсету үшін құрған ROSE қозғалтқышын ендіру модельдерінің орындалуын да басқаруға бейімдедік. Ендіру модельдерін қолдауға қажетті күш-жігерді азайту үшін ROSE LLM пен ендірулер арасындағы кодты агрессивті түрде қайта пайдаланады. Мысалы, pplx-embed қызмет көрсетуі мен Qwen3.5 LLM декодтауы барлығы бірдей kernel-дер арқылы өтеді. Бұл ортақ пайдалану бізге прототиптеу, бағалау және өндірістік inference үшін LLM-нен бастапқыда fine-tune жасалған ендіру моделіне оңай қызмет көрсетуге мүмкіндік береді.
Тығыз қабаттар үшін токен векторлары тәуелсіз өңделетіндіктен, ендіру және LLM inference бірдей болады. Назар аудару қабаттарында айырмашылықтар LLM талап ететін беттелген prefill және decode орнатуларымен қатар, ретсіз (ragged) кірістерді қолдауды қосу арқылы шешіледі. Ендіру моделіне қызмет көрсету кезінде біз KV кэшін іске қоспаймыз және толтыруды (padding) болдырмау үшін ragged форматын қолдайтын назар аудару kernel-дерінің түрлеріне жібереміз. Сәйкес түрлендіру және калибрлеу процедуралары да LLM модельдерімен ортақ пайдаланылады.
Ivy
Біздің inference HTTP прокси қабатымыз Ivy те өнімділікте маңызды рөл атқарады. Өндірісте сұрау жүктемелері әртүрлі болатындықтан, жеке сұрауларды жеке көшірмелерге бағыттау жүктеме теңгерімсіздігіне әкелуі мүмкін. Ivy үлкен пакеттік сұрауларды бөліктерге бөледі және оларды көшірмелер арасында жүктеме бойынша теңестіріп, пайдалануды жақсартады және кідірісті тегістейді. Біздің үй ішіндегі униграммдық токенизация бойынша соңғы жұмысымыз Ivy жүйесінде толығымен енгізіліп, дайын токенизаторлармен салыстырғанда кідірістерді айтарлықтай жақсартады.
...бірақ Kernel-дер әлі де маңызды
ROSE әртүрлі назар аудару бэкендтерін қолдайды. Әртүрлі kernel-дер белгілі бір есеп өлшемдеріне сәйкес келуі мүмкін. Уақыт өте келе біз ragged attention жүзеге асыру үшін FlashInfer 2, FlashInfer 3 және FlashAttention 4 kernel-дерін біріктірдік.

Жалпы алғанда, FlashAttention 4 жылдамырақ екенін көреміз. Дегенмен, FlashInfer 3 өте ұзын тізбек ұзындықтарында Qwen негізіндегі модельдерде одан асып түседі. Өнімділік пен баптау назар аудару бастарының (attention heads) саны мен өлшеміне байланысты өзгеруі мүмкін болғандықтан, біз бірнеше конфигурацияны қолдауды сақтаймыз және қызмет көрсету кезінде әр жағдайға жеке шешім қабылдаймыз.
Бенчмарктар
Біз бағалау деректер жиындарынан алынған нақты модель салмақтары мен кірістерінде BF16 дәлдігімен inference орындай отырып, vLLM v0.22.0 нұсқасымен салыстырамыз. Барлық уақыттық сынақтар cosine ұқсастығының ауытқуы 0.1% шегінде екенін растайтын қыздыру (warmup) сынақтарынан кейін орындалды.
Төмен кідірісті ендірулер (p50 / p90 / p99 / макс. мс)
Біз алдын ала токенизацияланған сұрау пакетінің 1 өлшемі, толығымен тізбекті сұраулар, 128, 512 және 4096 токен ұзындықтары үшін орындалу уақыттарын хабарлаймыз.

Төмен кідірісті бағалау (p50 / p90 / p99 / макс. мс)
Алдын ала токенизацияланған сұрау пакетінің өлшемдері 5, 25 және 50, токендердің тізбек ұзындығы 512.

Жоғары өткізу қабілетті ендірулер (emb/с)
Сұраныс пакетінің өлшемі 100, сұраныстарды жіберетін төрт қатарлас процесс, 512, 1024 және 4096 токен ұзындықтары.

Жоғары қатарлас ендірулер (p50 / p90 / p99 / макс. мс)
Тізбек ұзындығы 512, пакет өлшемі 1, бірақ біз 1, 2, 4, 8 және 16 қатарлас сұрау жібереміз. Бұл бенчмарк сонымен қатар Ivy арқылы токенизация шығындарын, сондай-ақ Ivy пен Tulip арасындағы желілік үстеме шығындарды қамтиды.

Қорытынды және болашақ жұмыс
Ivy, Tulip және ROSE құрамдас бөліктерінен тұратын қызмет көрсету инфраструктурасы Perplexity үшін ендірулерді төменірек кідіріспен және жақсырақ өткізу қабілетімен қамтамасыз етуге мүмкіндік береді, бұл дайын шешімдермен салыстырғанда төмен бағамен дәлірек іздеуге әкеледі.
Белгілі бір модельдерге назар аудару және бүкіл стекке иелік ету арқылы біз өнімділік пен икемділік арасындағы тиімді тепе-теңдікті сақтауға қажетті еркіндікке қол жеткіземіз, бұл ретте жоғары қайта пайдаланылатын және өнімді Rust примитивтерін көбірек жалпылама Python модельдеу кодымен араластырамыз. vLLM, SGLang және TokenSpeed сияқты көптеген ашық бастапқы inference қозғалтқыштары Rust және C++ сияқты тілдерді өз стегіне біріктіруде. Біз соңғы екі жылда Rust саласына инвестиция салдық және өнімділік пен қолжетімділік жағынан үлкен жетістіктерге қол жеткіздік. Ендіру іске асырудың көп бөлігін біздің LLM қызмет көрсету стегімен бөлісу арқылы біз ендіру модельдерін техникалық қызмет көрсетуге айтарлықтай инженерлік күш жұмсамастан, өткізу қабілеттілігінің артуына да қол жеткіземіз.
Модельдер дамыған сайын, біз CPU-ға байланысты және GPU-ға байланысты кідірістерді азайту үшін стегіміздің әрбір қабатын жақсартуды жалғастырамыз. Ivy пен Tulip ішіндегі біздің реттелетін gRPC негізіндегі протоколдар желілік кідірістерді азайту үшін байланысты баптауға мүмкіндік береді, ал ROSE есептеу өткізу қабілетін жақсарту үшін негіз береді. Сонымен қатар, экологиялық жүйеде еркін ағынды (free-threaded) Python қолдауы өскен сайын, үстеме шығындарды азайту үшін Python-Rust өзара әрекеттесуін одан әрі жақсарта аламыз.