Skip to main content

Penulis

Ashar Mirza - VoicePing Inc.

Rekap: Masalah

Dalam Bahagian 1, kami mengenal pasti bottleneck: perkhidmatan FastAPI kami menggunakan pekerja multiprocessing dengan baris gilir IPC untuk mengagihkan tugas terjemahan. Ini mewujudkan:
  • Overhead serialisasi baris gilir
  • Pertembungan pengiraan GPU antara proses pekerja
  • Corak penggunaan GPU tersentak
Garis dasar: 2.2 RPS pada 25 permintaan serentak Laluan ke hadapan: hapuskan multiprocessing dan gunakan inferens batch vLLM.

Percubaan 2: Batching Statik

Kami melaksanakan batching statik dalam proses pekerja sedia ada.

Pelaksanaan

# Dalam proses pekerja
MAX_BATCH_SIZE = 16
BATCH_TIMEOUT = 0.05  # 50ms

while True:
    batch_keys = []
    batch_tasks = []

    # Kumpul tugas pertama (menyekat)
    first_key = queue.get()
    batch_keys.append(first_key)
    batch_tasks.append(tasks[first_key])

    # Cuba kumpul lebih banyak tugas (tidak menyekat dengan timeout)
    batch_start = time.time()
    while len(batch_keys) < MAX_BATCH_SIZE:
        time_remaining = BATCH_TIMEOUT - (time.time() - batch_start)
        if time_remaining <= 0:
            break
        try:
            key = queue.get(timeout=time_remaining)
            batch_keys.append(key)
            batch_tasks.append(tasks[key])
        except Empty:
            break

    # Proses batch menggunakan vLLM
    results = translation_provider.translate_batch(
        texts=[t.text for t in batch_tasks],
        source_langs=[t.source_lang for t in batch_tasks],
        target_langs=[t.target_lang for t in batch_tasks]
    )
Perkara utama:
  • Saiz batch: 16 permintaan
  • Timeout: 50ms (jangan tunggu tanpa had untuk batch penuh)
  • vLLM memproses berbilang urutan bersama-sama
  • Masih menggunakan pekerja multiprocessing

Keputusan

Keputusan Batching Statik

Rajah 1: Batching statik memberikan peningkatan throughput dan masa respons yang ketara

Peningkatan throughput hampir 3x. Masa inferens setiap permintaan: 452ms -> 171ms.

Pertukaran

Kelebihan:
  • Peningkatan throughput besar
  • GPU digunakan dengan lebih baik
  • Pelaksanaan mudah
Kekurangan:
  • Penyekatan head-of-line: Semua permintaan menunggu yang paling perlahan
  • Dengan input panjang berubah-ubah, terjemahan pendek menunggu yang panjang
  • Contoh: [50 token, 50 token, 200 token] - dua pertama menunggu terjemahan 200-token
Ini adalah kemajuan yang baik, tetapi kami mahu menghapuskan isu penyekatan head-of-line.

Percubaan 3: Continuous Batching

Penyelesaian: AsyncLLMEngine vLLM dengan continuous batching.

Apakah Continuous Batching?

Tidak seperti batching statik, continuous batching menyusun batch secara dinamik:
  • Permintaan baharu menyertai pertengahan penjanaan
  • Permintaan selesai keluar segera (tidak menunggu yang lain)
  • Komposisi batch dikemas kini setiap token
  • AsyncLLMEngine vLLM mengendalikan ini secara automatik
Tiada penyekatan head-of-line. Terjemahan pendek kembali sebaik sahaja selesai.

Pelaksanaan

from vllm import AsyncLLMEngine, EngineArgs

engine_args = EngineArgs(
    model=model_id,
    max_num_seqs=64,  # Percubaan awal
    max_num_batched_tokens=16384,
    gpu_memory_utilization=0.3,
    enable_chunked_prefill=True,
)

engine = AsyncLLMEngine.from_engine_args(engine_args)

@app.post("/translate")
async def translate(request: TranslateRequest):
    result_generator = engine.generate(
        request.text,
        sampling_params,
        request_id=generate_id()
    )

    async for output in result_generator:
        final_output = output
    return TranslateResponse(translation=final_output.text)
Perubahan seni bina:
  • AsyncLLMEngine digunakan terus dalam FastAPI
  • vLLM mengendalikan batching secara dalaman melalui enjin continuous batching
  • Async/await tulen sepanjang masa

Semakan Realiti Ujian

Keputusan Awal (Input Seragam)

Kami menguji dengan input panjang standard seragam (panjang serupa):
Continuous Batching Seragam

Rajah 2: Continuous batching dengan input seragam menunjukkan throughput 15 RPS yang mengagumkan

15 RPS vs 2.2 garis dasar - peningkatan hampir 7x. Ini kelihatan hebat.

Input Panjang Berubah-ubah (Realiti)

Kemudian kami menguji dengan input panjang berubah-ubah yang realistik (10-200 token, campuran pendek dan panjang): Jalankan semula garis dasar dengan input berubah-ubah:
  • Beban sangat berat: 1.1 RPS (vs 2.2 RPS dengan seragam)
  • Malah garis dasar berprestasi lebih teruk dengan data realistik
Continuous batching (max_num_seqs=64) dengan input berubah-ubah:
  • Beban sangat berat: 3.5 RPS (dengan penalaan max_num_seqs=16)
  • Konfigurasi yang sama yang memberikan kami 15 RPS dengan input seragam
Standard vs Berubah-ubah

Rajah 3: Jurang prestasi antara data ujian seragam dan input panjang berubah-ubah realistik

Penalaan Konfigurasi

Prestasi lemah dengan max_num_seqs=64 membawa kami menganalisis metrik dalaman vLLM.

Apa Yang Kami Temui

# Metrik Prometheus vLLM yang kami pantau:
# - vllm:time_to_first_token_seconds (TTFT)
# - vllm:time_per_output_token_seconds (masa decode)
# - vllm:gpu_cache_usage_perc (penggunaan cache KV)
# - vllm:num_requests_running / waiting (kedalaman baris gilir)
Isunya:
  • Beban kerja sebenar: 2-20 permintaan serentak setiap pelayan (puncak pengeluaran ~20 setiap pelayan)
  • Konfigurasi: max_num_seqs=64
  • Keputusan: 60+ slot kosong mewujudkan overhead
Apa yang berlaku dengan konfigurasi terlalu besar:
  • Cache KV dipra-peruntukkan untuk 64 urutan
  • Penjadual vLLM menguruskan 64 slot tetapi hanya menggunakan 5-10
  • Masa decode setiap token meningkat
  • Memori terbuang pada slot urutan yang tidak digunakan
  • Overhead penjadual untuk slot kosong

Pendekatan Penalaan

Mengikut panduan penalaan continuous batching vLLM:
  1. Ukur taburan permintaan serentak sebenar dalam pengeluaran
  2. Mulakan dengan max_num_seqs=1, tingkatkan secara beransur-ansur: 2 -> 4 -> 8 -> 16 -> 32
  3. Pantau masa decode dan latensi ekor pada setiap langkah
  4. Berhenti apabila prestasi merosot
max_num_seqsKeputusan
8Latensi baik, tetapi throughput terhad
16Keseimbangan terbaik
32Masa decode meningkat, latensi ekor lebih teruk

Konfigurasi Akhir

from translation_lib.config import AsyncVLLMTranslationProvider

provider = AsyncVLLMTranslationProvider(
    model_name=model_id,
    revision=model_revision,
    gpu_memory_utilization=0.3,  # ~10GB pada RTX 5090
    max_num_seqs=16,  # Bersaiz betul kepada beban kerja sebenar setiap pelayan
    huggingface_token=hf_token,
    supported_language_pairs=None,  # Model berbilang bahasa
)

await provider.initialize_engine()

Rasional Konfigurasi

max_num_seqs=16:
  • Puncak pengeluaran: ~20 permintaan serentak setiap pelayan
  • Ujian: Disahkan sehingga 25 serentak
  • Menyediakan ruang kepala tanpa membazir sumber
  • Overhead penjadual sepadan dengan beban sebenar
max_num_batched_tokens=8192:
  • Dikurangkan dari lalai 16384
  • Lebih sesuai untuk panjang urutan purata kami
  • Mengurangkan tekanan memori
gpu_memory_utilization=0.3:
  • Memperuntukkan ~10GB VRAM untuk model + cache KV pada RTX 5090 (32GB)
  • Dijejak melalui vllm:gpu_cache_usage_perc
  • Seimbang untuk konfigurasi kami
Prinsipnya: padankan konfigurasi kepada beban kerja sebenar anda, bukan had teori.
Perjalanan Throughput

Rajah 4: Kemajuan throughput melalui semua percubaan pengoptimuman

Keputusan Pengeluaran

Kami menggunakan konfigurasi yang dioptimumkan ke pengeluaran (GPU RTX 5090).

Sebelum vs Selepas

MetrikSebelum (Multiprocessing)Selepas (AsyncLLM Dioptimumkan)Perubahan
Throughput9.0 RPS16.4 RPS+82%
Penggunaan GPUTersentak (93% -> 0% -> 93%)Konsisten 90-95%Stabil
Perbandingan Pengeluaran

Rajah 5: Keputusan penggunaan pengeluaran menunjukkan peningkatan throughput 82%

Perbandingan P95

Rajah 6: Peningkatan latensi P95 merentasi percubaan pengoptimuman

Evolusi Masa Respons

Rajah 7: Evolusi masa respons dengan input panjang berubah-ubah

Peningkatan bertahan dalam pengeluaran. Dari 9 RPS ke 16.4 RPS di bawah trafik sebenar.

Ringkasan

Apa Yang Berkesan

Continuous batching vLLM
  • AsyncLLMEngine mengendalikan batching secara automatik
  • Tiada overhead pengumpulan batch manual
  • Integrasi async/await langsung dengan FastAPI
Konfigurasi bersaiz betul
  • max_num_seqs=16 (sepadan dengan beban kerja sebenar setiap pelayan)
  • Bukan 64 (maksimum teori yang mewujudkan overhead)
  • gpu_memory_utilization=0.3 untuk peruntukan 10GB
Diuji dengan data realistik
  • Input panjang berubah-ubah mendedahkan isu konfigurasi
  • Data ujian seragam memberikan 15 RPS yang mengelirukan
Memantau metrik vLLM
  • Penggunaan cache KV
  • Masa decode setiap token
  • Kedalaman baris gilir
  • Membimbing keputusan konfigurasi

Perjalanan Lengkap

PendekatanThroughputvs Garis DasarNota
Garis dasar (multiprocessing)2.2 RPS-Overhead IPC, pertembungan GPU
Dua pekerja2.0 RPS-9%Memburukkannya
Batching statik5.9 RPS+168%Penyekatan head-of-line
Async (64, seragam)15.0 RPS+582%Data ujian mengelirukan
Async (16, berubah-ubah)3.5 RPS+59%Realistik, tetapi penalaan diperlukan
Dioptimumkan akhir10.7 RPS+386%Pengesahan staging
Pengeluaran16.4 RPS+82%Trafik sebenar, RTX 5090