Kısa cevap

Bir yapay zeka arama ürününde getirme (retrieval) kalitesini iki şey sınırlıyor: gömme modelinin ne kadar iyi olduğu ve o modeli bir dizin üzerinde ne kadar ucuza çalıştırabildiğiniz. Birincisi araştırma sorusu, ikincisi mühendislik sorusu — ve ürünü asıl ayakta tutan çoğu zaman ikincisi.

Perplexity mühendislik ekibi bu ikinci tarafı ayrıntılarıyla yayımladı: arama, Computer ve API platformunda kullanılan pplx-embed ve sıralama modellerinin arkasındaki servis altyapısı. Bu yazı o altyapıdan çıkan genel dersleri anlatıyor.

Gömme nedir?

Gömme, bir metni sayı dizisine çeviren işlem. Amaç anlamı korumak: birbirine yakın anlamdaki iki cümlenin sayı dizileri de birbirine yakın çıkıyor. Arama böyle çalışıyor — sorgu da belgeler de aynı uzaya yerleştiriliyor ve en yakın komşular getiriliyor. Anahtar kelime eşleşmesinden farkı bu: "araba kiralama" sorgusu, içinde o kelimeler hiç geçmeyen bir "oto kiralama" sayfasını da bulabiliyor.

Buradaki kritik ayrıntı model boyutu. Gömme modelleri küçük Transformer'lar; genellikle bir milyar parametrenin altında. Küçük olmaları maliyeti düşürüyor ama başka bir sorun yaratıyor: model o kadar küçük ki, onu çalıştırmanın etrafındaki iş modelin kendisinden pahalı hâle gelebiliyor.

İki farklı trafik, tek motor

Perplexity gömme servisini iki iş yükü olarak tanımlıyor ve aradaki fark her şeyi belirliyor:

İş yüküNe zamanNeyi eniyiler
Toplu gömmeVektör veritabanı kurulurken ya da yeniden dizinlenirkenİş hacmi — maliyeti düşürmek
Çevrimiçi gömmeSorgu anında, tek ve kısa metinGecikme — hızlı cevap
PuanlamaVektör aramasından sonra belgeleri sıralarkenİkisinin dengesi

Ekibin en dikkate değer kararı şu: gömme için ayrı bir motor kurmadılar. Gerekçe teknik ve zarif — toplu gömme, hesaplama sınırlı bir "ön doldurma"ya (prefill) benziyor; çevrimiçi gömme ise çoğu zaman birkaç jetonluk olduğu için bellek sınırlı bir "çözme"ye (decode) benziyor. Yani büyük dil modeli yığınındaki mevcut çekirdekler zaten doğru şekle sahipti; yeniden kullanıldılar.

Üç servis: Ivy, Tulip ve ROSE

Bir isteği üç servis karşılıyor ve bu ayrım tesadüfi değil:

  • Ivy — Rust ile yazılmış HTTP geçidi. CPU tarafındaki işi yapıyor: JSON ayrıştırma, jetonlaştırma, girdi şablonlama, yığın bölme. Büyük yığınlı istekleri parçalara ayırıp kopyalar arasında yük dengeliyor.
  • Tulip — çıkarım sunucusu arayüzü. Rust, tokio ve tonic ile kurulmuş bir gRPC sunucusu; zamanlama ve yığınlamayı yapıp motora gönderiyor.
  • ROSE — asıl çıkarım motoru. Ağırlıklı olarak Python; çekirdekleri, katmanları ve model tanımlarını barındırıyor, CUDA grafiklerini yönetiyor.

Buradaki desen genellenebilir: CPU işi ile GPU işi ayrı servislere ayrılmış ve CPU tarafı sistem dili ile yazılmış. Jetonlaştırma ve JSON ayrıştırma küçük modellerde GPU'yu bekletebilecek kadar pahalı; o yüzden Python'da kalmamışlar.

Zamanlayıcı neden bilerek basit?

Tulip istekleri geldikleri sırayla alıyor — karmaşık bir öncelik şeması yok. Bu tembellik değil, ölçüme dayanan bir karar.

Perplexity'nin servis ettiği dizi uzunluklarında ve küçük gömme modellerinde, yoğun katmanların doğrusal maliyeti dikkat mekanizmasının karesel maliyetine baskın çıkıyor. Sonuç: gecikme dizi sayısıyla değil, kabaca jeton sayısıyla orantılı. Bir yığın GPU'yu doyurduktan sonra — bir milyar parametrenin altındaki bir modelde yaklaşık 512 jeton — içine daha fazla dizi tıkmak verimi artırmıyor.

Buradan çıkan pratik kural şu: kendi yığınınızı ölçmeden zamanlayıcı optimize etmeyin. Karesel terimin baskın olduğu bir yerde akıllı zamanlama kazandırır; doğrusal terimin baskın olduğu bir yerde aynı emek boşa gider.

Asıl darboğaz: çekirdek başlatma

Küçük yığınlarda şaşırtıcı bir şey oluyor: CPU'nun GPU çekirdeklerini başlatma maliyeti, GPU'nun asıl hesaplama süresini geçebiliyor. Yani GPU işi bitirmiş, sıradaki komutu bekliyor.

Çözüm CUDA grafikleri. Modelin tamamı tek bir grafiğe kaydediliyor ve bütün başlatmalar tek bir sürücü çağrısına düşüyor. Gömme modelleri küçük olduğu için GPU işinin başlatma maliyetini geçtiği eşik oldukça geç geliyor: binlerce jeton ve onlarca dizi.

Ama grafiklerin bir bedeli var: her yapılandırma için ayrı yakalanmaları gerekiyor. Jeton sayıları 64 ya da 256'nın katlarına yuvarlansa bile ortaya binlerce grafik ve model başına dakikalarca yakalama süresi çıkıyor. Perplexity'nin çözümü gecikmeli yakalama: her yapılandırma önce bir kez normal çalışıyor, ikinci isabetinde yakalanıyor. Bu, açılıştaki p99 gecikmesinden feragat ediyor ama dakikalarca süren işi saatlere yayıyor.

Beklemeyi beklememek

İkinci mekanizma LazyTensor. Normalde bir çıkarım adımı GPU'nun bitirmesini bekler ve o süre boyunca CPU boş durur. LazyTensor bunun yerine sabitlenmiş bir bellek tamponu, eşzamansız bir kopyalama ve bir CUDA olayı takip ediyor; adım fonksiyonu beklemek yerine hemen geri dönüyor.

Sonuç: bir Rust görevi N numaralı yığını beklerken CPU N+1'i hazırlamaya başlıyor. Kazanç yeni bir donanımdan değil, boş geçen zamanı doldurmaktan geliyor.

Çekirdek seçimi hâlâ elle yapılıyor

ROSE düzensiz uzunluktaki girdiler için birden fazla dikkat arka ucu destekliyor: FlashInfer 2, FlashInfer 3 ve FlashAttention 4. Ekibe göre FlashAttention 4 genelde daha hızlı, ama çok uzun dizi uzunluklarında Qwen tabanlı modellerde FlashInfer 3 onu geçiyor — bu yüzden seçim vaka bazında yapılıyor.

Bir ayrıntı daha: gömme modeli servis ederken ROSE bir KV önbelleği hiç oluşturmuyor ve dolgu yapmamak için düzensiz dikkat çeşitlerine yönlendiriyor. Gömmede üretilecek bir sonraki jeton yok; büyük dil modelinden devralınan yapının bu parçası burada sadece bellek harcardı.

Neden bu iş bu kadar zor?

Gömme servisinin zorluğu, sezginin tersine, modelin büyüklüğünden değil küçüklüğünden geliyor. Büyük bir dil modelinde GPU o kadar çok iş yapıyor ki etrafındaki her şey gürültüde kayboluyor: birkaç milisaniyelik jetonlaştırma, saniyelerce süren üretimin yanında görünmez. Küçük bir gömme modelinde ise durum tersine dönüyor — model işini mikrosaniyelerde bitiriyor ve etrafındaki her gecikme faturaya doğrudan yazılıyor.

Buna bir de ölçek biniyor. Bir arama ürününde gömme iki kez çalışıyor: bir kez dizin kurulurken milyarlarca belge için, bir kez de her sorguda. İlkinde tek bir yüzdelik verimlilik kazancı doğrudan donanım faturasına yansıyor; ikincisinde tek bir milisaniye doğrudan kullanıcının beklediği süreye ekleniyor. Aynı modelin iki farklı ekonomisi var ve ikisi zıt yönde optimize edilmek istiyor.

Ölçüm neye karşı yapıldı?

Bir mühendislik yazısının en kolay atlanan kısmı karşılaştırmanın nasıl kurulduğu, oysa sonucun ne kadar anlamlı olduğunu o belirliyor.

Perplexity kıyaslamayı vLLM 0.22.0'a karşı, BF16 hassasiyetinde, gerçek model ağırlıkları ve değerlendirmeden türetilmiş gerçek girdilerle yapıyor. Isınma koşuları kosinüs benzerliğindeki sapmanın yüzde 0,1 içinde kaldığını doğruluyor — yani hız kazancı çıktıyı bozarak elde edilmemiş. Dört ayrı takım ölçülüyor: düşük gecikmeli gömme (tek istek; 128, 512 ve 4.096 jeton), düşük gecikmeli puanlama (512 jetonda 5, 25 ve 50'lik yığınlar), yüksek hacimli gömme (100'lük yığın, dört eşzamanlı süreç) ve yüksek eşzamanlılık (1 ilâ 16 eşzamanlı istek, Ivy'nin jetonlaştırması ve ağ yükü dahil).

Son parantez önemli: ağ ve jetonlaştırma yükünün ölçüme dahil edilmesi, rakamın laboratuvar değil üretim rakamı olduğunu gösteriyor. Yalnızca GPU süresini ölçen bir kıyaslama, tam da bu yazının anlattığı sorunları görünmez kılardı.

Bundan ne çıkarmalı?

Kendi getirme yığınını kuran biri için aktarılabilir dersler:

  • Önce nerede zaman geçtiğini ölçün. Küçük modellerde darboğaz sıklıkla modelin kendisi değil, etrafındaki başlatma ve veri hazırlama işi.
  • Ayrı motor kurmadan önce şekli sorun. Toplu gömme ön doldurmaya, tekil sorgu çözmeye benziyorsa mevcut çekirdekler zaten uygundur.
  • Yığını jeton sayısına göre kurun, dizi sayısına göre değil. Doyma noktasından sonra dizi eklemek kazandırmıyor.
  • CPU tarafını hafife almayın. Jetonlaştırma ve ayrıştırma GPU'yu bekletebiliyor; bu iş sistem diline taşınabilir.
  • Kıyaslamayı kendi verinizle yapın. Perplexity karşılaştırmalarını gerçek ağırlıklar ve değerlendirmeden türetilmiş girdilerle, kosinüs benzerliği sapması yüzde 0,1 içinde kalacak şekilde doğrulayarak yapıyor.

Özetle

Perplexity'nin anlattığı hikâyenin özeti şu: GPU tarafında gömme çıkarımı olgun donanımda büyük ölçüde yakınsamış durumda — motorlar arasındaki fark kapanmış. Kazanç artık modelin etrafındaki koşumda: CUDA grafiği yönetimi, eşzamansız sonuç takibi ve hızlı bir istek yolu.

Bu, alan için genellenebilir bir gözlem. Model seçimi kolay kısım; ürünü ayakta tutan kısım, o modeli günde milyonlarca kez çalıştırırken boşa geçen milisaniyeleri toplamak.

Bir uyarıyla bitirelim: bu yazıdaki sayılar ve tercihler Perplexity'nin kendi ölçümlerinden geliyor ve kendi iş yüküne göre kalibre edilmiş. Dizi uzunluklarınız, model boyutunuz ve donanımınız farklıysa doyma noktası da, doğru zamanlayıcı da değişir. Aktarılabilir olan rakamlar değil, sorulan sorular.