Cara Mengukur Token per Detik pada LLM Lokal
Ukur token per detik dengan uji konkurensi, lalu bandingkan hasilnya dengan biaya API. GPU sewaan hanya lebih hemat setelah melewati batas throughput tertentu.
Mengapa token per detik menentukan apakah GPU layak digunakan
Token per detik adalah laju server menghasilkan teks keluaran. Angka ini menentukan apakah menyewa GPU lebih murah daripada membayar API berdasarkan jumlah token. Biaya GPU dihitung per jam, baik GPU sedang digunakan maupun menganggur. Biaya API terkelola dihitung berdasarkan jumlah token. Jadi, GPU hanya lebih hemat jika Anda mempertahankan laju keluaran yang cukup tinggi selama sebagian besar jam yang Anda bayar.
Artinya, Anda memerlukan hasil pengukuran, bukan angka yang Anda baca di tempat lain. Halaman ini menjelaskan empat angka yang perlu dicatat, lalu memberikan perintah untuk mendapatkannya serta perhitungan untuk mengubahnya menjadi keputusan.
Mengapa angka token per detik yang dipublikasikan bukan angka Anda
DigitalOcean memublikasikan angka throughput pada Juli 2026 untuk satu NVIDIA H200 yang menjalankan llama3.3-70b-instruct dalam FP8 (floating point 8-bit) menggunakan vLLM. Angka tersebut berguna, tetapi bukan angka Anda.
The data behind this chart
[
{
"config": "H200, one stream",
"tok_s": "47"
},
{
"config": "H100, saturated",
"tok_s": "236"
},
{
"config": "H200, saturated",
"tok_s": "2,036"
},
{
"config": "H200, saturated, in+out",
"tok_s": "4,071.6"
}
]Setiap baris di atas dikutip dari halaman tersebut. Dua di antaranya adalah batas bawah dari rentang yang dicantumkan, jadi perlakukan kedua angka itu sebagai batas minimum. Tidak ada angka dalam tabel ini yang kami ukur sendiri.
Mulai dari dua baris terakhir. Angka utama adalah 4,071.6 tok/s, sedangkan laju yang hanya menghitung output adalah 2,036 tok/s. Angka utama menghitung token input dan output secara bersamaan. Pengujian tersebut menggunakan 1,024 token input dan 1,024 token output, sehingga hampir tepat setengah dari angka utama merupakan output. Pembagian ini penting karena Anda ditagih berdasarkan output, dan bagian ini juga lebih lambat. Prefill, yaitu membaca prompt, memproses semua token input dalam satu lintasan. Decode, yaitu menulis jawaban, menghasilkan satu token setiap kali. Angka throughput total merata-ratakan komponen yang murah dan komponen yang mahal.
Sekarang baris pertama. H200 yang sama saat melayani satu permintaan pada satu waktu menghasilkan 47 tok/s. Dengan demikian, angka saat GPU jenuh lebih dari 40 kali lebih tinggi pada perangkat keras yang sama. Perbedaan ini terjadi karena satu langkah decode membuat GPU menunggu memori hampir sepanjang waktu. Permintaan yang berjalan secara bersamaan mengisi waktu menganggur tersebut. Baris kedua, 236 tok/s, adalah satu H100 dengan model yang sama. Kinerjanya dibatasi oleh KV cache (cache key dan value, yaitu memori per permintaan yang disimpan percakapan pada kartu grafis). Kartu 80 GB dapat menampung lebih sedikit permintaan bersamaan untuk model 70B, sehingga tingkat kejenuhannya lebih rendah.
Jika Anda mengganti model atau mengubah rasio input terhadap output, semua angka di atas akan berubah. Angka yang dipublikasikan membantu menetapkan ekspektasi, bukan anggaran Anda. Aturan yang sama berlaku saat membandingkan VPS secara jujur berdasarkan disk dan jaringan.
Empat angka yang penting
- Time to first token, TTFT. Jeda antara pengiriman request dan tibanya token output pertama. Nilai ini merupakan gabungan waktu prefill dan waktu dalam antrean. Pengguna merasakan metrik ini secara langsung.
- Output token per detik, per stream. Kecepatan satu jawaban ditulis setelah prosesnya dimulai. Di atas sekitar 20 tok/s, kecepatannya sudah melampaui kecepatan baca sebagian besar orang, sehingga peningkatan lebih lanjut hanya memberi sedikit manfaat.
- Total throughput output saat jenuh. Jumlah seluruh stream yang berjalan secara bersamaan ketika server sepenuhnya terbebani. Ini adalah angka kapasitas dan menjadi faktor yang menentukan biaya GPU.
- p50 dan p99 TTFT saat concurrency. p50 adalah request di posisi tengah. p99 adalah nilai yang berada di atas 99 dari setiap 100 request. Dampak antrean selalu pertama kali terlihat pada p99.
Dua metrik pertama membaik ketika server tidak sibuk. Metrik ketiga membaik ketika server sibuk. Ketiganya saling berlawanan. Karena itu, tidak ada satu angka yang dapat menggambarkan server serving.
Tetapkan panjang input dan output sebelum melakukan pengukuran
Throughput bergantung pada bentuk trafik. Prompt dengan 4,000 token dan jawaban dengan 50 token merupakan beban yang didominasi prefill. Prompt dengan 200 token dan jawaban dengan 2,000 token merupakan beban yang didominasi decode. Server yang sama akan melaporkan token per detik yang sangat berbeda untuk kedua kondisi tersebut. Karena itu, pilih satu rasio, tuliskan rasio tersebut di samping setiap angka yang dicatat, dan jangan pernah membandingkan hasil dari rasio yang berbeda. Rasio 1,024 token input dan 1,024 token output merupakan nilai default yang wajar karena beberapa vendor menerbitkan hasil pada rasio tersebut. Jika Anda mengetahui trafik sebenarnya, gunakan trafik sebenarnya.
Tetapkan panjang output juga. Model yang mencapai stop token setelah 60 token menghasilkan proses yang lebih singkat dan tampak lebih cepat karena TTFT mencakup proporsi yang lebih besar dari keseluruhan proses. Flag --ignore-eos pada klien benchmark vLLM membuat setiap permintaan menghasilkan jumlah token yang diminta secara tepat, sehingga kedua proses dapat dibandingkan. Pemilihan model memengaruhi angka ini lebih besar daripada flag apa pun: menjalankan model Qwen 3 pada satu GPU VPS membahas aspek memori dalam pemilihan tersebut.
Ukur satu aliran terlebih dahulu
Mulai dari kasus paling sederhana. Ini adalah pemeriksaan kewajaran sekaligus batas atas. Ollama menampilkan waktunya sendiri.
ollama run llama3.1:8b --verbose "Write 200 words about disk scheduling."Baris yang perlu dibaca adalah eval rate, yaitu jumlah token output per detik. prompt eval rate adalah laju prefill, sedangkan load duration adalah waktu yang digunakan untuk memuat model ke VRAM. Pada pemanggilan pertama setelah cold start, load duration bernilai besar sehingga total duration menyesatkan. Jalankan perintah tersebut dua kali dan baca hasil kedua. Secara default, Ollama membongkar model yang tidak aktif setelah lima menit. Jeda panjang antara dua proses akan mengembalikan Anda ke kondisi cold start.
Kolom yang sama tersedia melalui API dan lebih mudah digunakan dalam skrip.
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "llama3.1:8b",
"prompt": "Write 200 words about disk scheduling.",
"stream": false
}' | jq '{eval_count, eval_duration,
tok_s: (.eval_count / (.eval_duration / 1000000000))}'eval_duration adalah nanodetik. Jadi, membaginya dengan 1,000,000,000 akan menghasilkan detik. Pembagian tersebut persis seperti yang ditetapkan dalam dokumentasi API Ollama untuk menghitung token per detik. Jika server belum berjalan, self-hosting LLM dengan Ollama pada VPS menjelaskan instalasi dan unit systemd.
TTFT memerlukan permintaan streaming, dan curl dapat mengukur waktunya untuk Anda.
curl -s -o /dev/null -N \
-w 'ttfb=%{time_starttransfer}s total=%{time_total}s\n' \
http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"Qwen/Qwen3-8B",
"messages":[{"role":"user","content":"Write 200 words about disk scheduling."}],
"stream":true,"max_tokens":256}'time_starttransfer adalah saat byte pertama dari body respons tiba. Pada streaming chat completion, byte tersebut merupakan bagian dari server-sent event pertama. Event itu dapat berupa token konten pertama atau delta yang hanya berisi role dan dikirim tepat sebelumnya. Jadi, anggap nilai tersebut sebagai TTFT dengan selisih sekitar satu event. Nilai ini cukup akurat untuk membandingkan dua proses pada server yang sama.
Angka dari satu aliran membuat kemampuan server terlihat lebih tinggi dalam dua hal. TTFT berada pada kondisi terbaik karena tidak ada permintaan lain yang mengantre di depan Anda. Laju per aliran juga berada pada kondisi terbaik karena seluruh kartu melayani satu permintaan. Keduanya tidak menunjukkan beban yang dapat ditangani server.
Cara menjalankan sweep konkurensi?
Sweep menjalankan satu workload tetap dengan tingkat konkurensi yang meningkat, lalu mencatat hasil pada setiap tahap. vLLM menyediakan client untuk ini dan menggunakan OpenAI API, sehingga client tersebut juga dapat digunakan dengan Ollama dan layanan lain yang kompatibel dengan OpenAI.
vllm bench serve \
--backend openai-chat \
--base-url http://127.0.0.1:8000 \
--endpoint /v1/chat/completions \
--model Qwen/Qwen3-8B \
--dataset-name random \
--random-input-len 1024 \
--random-output-len 1024 \
--ignore-eos \
--num-prompts 160 \
--max-concurrency 16 \
--percentile-metrics ttft,tpot,itl,e2el \
--metric-percentiles 50,99--max-concurrency membatasi jumlah request yang sedang diproses, dan nilainya adalah variabel yang Anda sweep. --num-prompts adalah jumlah total request yang dikirim. Pertahankan nilainya sekitar sepuluh kali tingkat konkurensi untuk mendapatkan rata-rata yang stabil. Ringkasan menampilkan Output token throughput (tok/s): dan Total token throughput (tok/s):, lalu Mean TTFT (ms):, Median TTFT (ms):, dan P99 TTFT (ms): di bawah heading Time to First Token.
Output tersebut tidak menampilkan laju per stream, tetapi nilainya dapat dihitung dengan satu pembagian. Mean TPOT (ms): adalah waktu rata-rata per output token setelah token pertama. Jadi, 25 ms per token berarti 40 token per detik per stream. Membagi throughput output dengan tingkat konkurensi menghasilkan nilai yang sama.
Selanjutnya, lakukan loop dan simpan setiap hasil.
for C in 1 4 16 32 64 128; do
vllm bench serve \
--backend openai-chat \
--base-url http://127.0.0.1:8000 \
--endpoint /v1/chat/completions \
--model Qwen/Qwen3-8B \
--dataset-name random \
--random-input-len 1024 \
--random-output-len 1024 \
--ignore-eos \
--num-prompts $(( C * 10 )) \
--max-concurrency "$C" \
--percentile-metrics ttft,tpot,itl,e2el \
--metric-percentiles 50,99 \
--save-result --result-filename "sweep-c$C.json"
doneMembaca file JSON yang disimpan
Setiap proses menulis satu file. Karena itu, ambil field yang diperlukan dari semua file sekaligus.
for f in sweep-c*.json; do
jq -r --arg f "$f" \
'[$f, .output_throughput, .total_token_throughput,
.median_ttft_ms, .p99_ttft_ms] | @tsv' "$f"
doneoutput_throughput adalah jumlah output token per detik. total_token_throughput menambahkan kembali input token, sehingga pada rasio 1:1 nilainya mendekati dua kali lipat. p99_ttft_ms hanya ada karena --metric-percentiles menyertakan 99. Jika Anda meminta percentile yang tidak diminta sebelumnya, jq mencetak null.
Apa yang sebenarnya ditunjukkan oleh uji concurrency?
The data behind this chart
[
{
"label": "1 stream",
"per_stream_tok_s": 92,
"total_tok_s": 92,
"ttft_p50_ms": 48,
"ttft_p99_ms": 61
},
{
"label": "4 streams",
"per_stream_tok_s": 88,
"total_tok_s": 352,
"ttft_p50_ms": 71,
"ttft_p99_ms": 96
},
{
"label": "16 streams",
"per_stream_tok_s": 71,
"total_tok_s": 1136,
"ttft_p50_ms": 152,
"ttft_p99_ms": 244
},
{
"label": "32 streams",
"per_stream_tok_s": 54,
"total_tok_s": 1728,
"ttft_p50_ms": 287,
"ttft_p99_ms": 498
},
{
"label": "64 streams",
"per_stream_tok_s": 34,
"total_tok_s": 2176,
"ttft_p50_ms": 611,
"ttft_p99_ms": 1240
},
{
"label": "128 streams",
"per_stream_tok_s": 18,
"total_tok_s": 2304,
"ttft_p50_ms": 1490,
"ttft_p99_ms": 3820
}
]6 baris tersebut menggambarkan bentuk hasil uji pada GPU sewaan kecil yang realistis, dengan orde besaran yang masuk akal. Itu bukan pengukuran server Anda dan bukan angka dari vendor. Jalankan loop di atas, lalu ganti dengan hasil Anda.
Perhatikan bentuknya karena bentuk tersebut dapat digeneralisasi. Dengan satu stream, seluruh GPU menghasilkan 92 token per detik dengan p99 TTFT sebesar 61 ms. Pada 128 streams, totalnya mencapai 2304 token per detik, 25 kali lebih tinggi, sementara setiap stream turun menjadi 18 token per detik dan p99 TTFT mencapai 3820 ms. Throughput total meningkat karena batching mengubah waktu tunggu memori yang tidak produktif menjadi pekerjaan yang berguna. Kecepatan per stream menurun karena komputasi yang sama kini digunakan bersama.
Penggandaan terakhir menunjukkan batasnya. Peralihan dari 64 ke 128 stream menambah total throughput kurang dari 6 persen, sementara p99 TTFT meningkat sekitar tiga kali lipat. Ini berarti KV cache sudah penuh dan request masuk ke antrean, bukan diproses. Titik operasi yang berguna berada lebih awal: pada 32 stream, GPU masih menghasilkan 1728 token per detik, yaitu 75 persen dari puncaknya, dengan kecepatan 54 token per detik per stream dan p99 TTFT sebesar 498 ms. Laporkan titik tersebut sebagai kapasitas Anda. Nilai puncak kurva bukan kapasitas yang dapat digunakan untuk melayani pengguna.
Ollama dan vLLM tidak mengukur hal yang sama
Jalankan pengujian tersebut pada server Ollama dengan konfigurasi default. Totalnya hampir tidak berubah. OLLAMA_NUM_PARALLEL secara default bernilai 1, sehingga satu permintaan berjalan sementara permintaan lainnya menunggu. Antrean inilah yang membuat TTFT p99 meningkat, sedangkan total output tetap. Tingkatkan nilainya sebelum melakukan pengukuran apa pun.
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_NUM_PARALLEL=8"Mulai ulang dengan sudo systemctl restart ollama, lalu pastikan model masih muat. Setiap slot paralel memperoleh bagiannya sendiri dari context window. Karena itu, dokumentasi Ollama menyebutkan bahwa context 2K dengan 4 permintaan paralel mengalokasikan 8K. Jika jumlah slot dinaikkan terlalu tinggi, model akan melampaui kapasitas VRAM. Periksa ollama ps: kolom PROCESSOR yang berisi nilai seperti 48%/52% CPU/GPU berarti sebagian model berada pada CPU. Dalam kondisi ini, throughput akan menurun saat concurrency ditambah, bukan meningkat. Setelah seluruh slot paralel terpakai, permintaan akan masuk antrean hingga OLLAMA_MAX_QUEUE, yang secara default bernilai 512. Setelah batas tersebut tercapai, server mengembalikan respons 503.
vLLM menggunakan continuous batching. Dengan demikian, vLLM memasukkan permintaan baru ke dalam batch yang sedang berjalan saat slot tersedia. Kurvanya terus meningkat hingga KV cache habis. Ollama dioptimalkan untuk satu model, satu mesin, dan biaya penyiapan yang rendah. Karena itu, kedua engine tersebut memberikan hasil yang berbeda pada pengujian yang sama. Perbedaan inilah yang menjadi pokok bahasan Perbandingan Ollama dan vLLM sebagai engine serving. Catat engine dan versinya untuk setiap angka yang dihasilkan.
Lima cara mengukur hal yang keliru
- Klien berada jauh. Benchmark dari laptop melalui Internet menambahkan waktu pulang-pergi koneksi Anda ke setiap TTFT, sehingga yang diukur adalah koneksi rumah Anda. Jalankan klien di region yang sama dengan server.
- Model belum dimuat ke memori. Permintaan pertama memerlukan waktu untuk memuat bobot. Pada vLLM, permintaan pertama juga dapat memerlukan waktu untuk menangkap graph. Kirim satu batch pemanasan, lalu abaikan hasilnya.
- Prefix caching memberikan hasil untuk Anda. vLLM mengaktifkan prefix caching otomatis secara default. Karena itu, pengiriman prompt yang sama berulang kali mengukur cache, bukan prefill, dan TTFT turun menjadi sebagian kecil dari nilai sebenarnya.
--dataset-name randommenghindari hal ini karena setiap prompt berbeda. Untuk memastikannya, jalankan server dengan--no-enable-prefix-caching. - Output terlalu pendek. Dengan jawaban sepanjang 32 token, TTFT mendominasi setiap permintaan sehingga tokens per second Anda sebenarnya menggambarkan prefill. Gunakan
--ignore-eosdengan panjang output yang realistis. - Anda melaporkan concurrency 1. Angka ini paling menguntungkan di lembar hasil dan tidak mencerminkan biaya.
Ubah angka hasil pengukuran menjadi keputusan
Gunakan throughput output jenuh dari sweep, bukan laju single-stream, lalu bandingkan dengan harga per token. Titik impasnya diperoleh melalui satu pembagian:
break_even_tok_s = (price_per_hour / price_per_million_output_tokens) * 1000000 / 3600Terapkan perhitungan ini pada harga DigitalOcean bulan July 2026. Dedicated inference endpoint H200 mereka berharga $4.47 per jam, sedangkan padanan serverless-nya berharga $0.65 per sejuta token. Jadi, 4.47 dibagi 0.65 menghasilkan 6.88 juta token per jam. Jika dibagi 3,600 detik, hasilnya sekitar 1,910 token output per detik. Harga tersebut berasal dari mereka. Pembagiannya adalah perhitungan kami.
Kata yang menentukan adalah sustained. Mencapai 1,910 token per detik saat jenuh selama dua jam sehari bukan berarti 1,910 token per detik secara sustained, karena Anda juga membayar 22 jam lainnya. Titik perbandingan DigitalOcean untuk GPU Droplet yang lebih murah, yaitu $3.44 per jam, berada pada utilisasi rata-rata sustained sebesar 72.2 persen. Di bawah angka tersebut, harga per token lebih menguntungkan. Jam GPU yang idle, bukan token yang lambat, biasanya membuat self-hosting tidak ekonomis.
Jadi, keputusan Anda memiliki dua masukan. Sweep memberikan batas atas. Pola trafik Anda memberikan fraksi dari batas atas yang benar-benar Anda gunakan. Kalikan keduanya, lalu bawa hasilnya ke perbandingan titik impas GPU VPS dan API per token untuk melihat jawaban berdasarkan volume Anda.
FAQ
Berapa tokens per second yang baik untuk LLM yang di-hosting sendiri?
Ada dua jawaban karena metrik ini memiliki dua fungsi. Bagi satu orang yang membaca output, lebih dari sekitar 20 output tokens per second per stream sudah lebih cepat daripada kecepatan membaca, sehingga nilai yang lebih tinggi tidak banyak membantu. Untuk biaya, angka yang penting adalah total output throughput saat jenuh. Nilai yang baik adalah angka yang membuat Anda melewati titik impas. Dengan biaya $0.65 per million tokens pada server seharga $4.47 per jam, batas tersebut berada di sekitar 1,910 output tokens per second secara berkelanjutan pada harga July 2026. Satu stream pada model besar tidak pernah mencapainya. Itulah alasan batching digunakan.
Mengapa throughput Ollama saya tetap saat menambahkan request bersamaan?
OLLAMA_NUM_PARALLEL secara default bernilai 1, sehingga server menjalankan satu request pada satu waktu untuk setiap model dan mengantrekan sisanya, hingga OLLAMA_MAX_QUEUE (512 secara default), sebelum mengembalikan 503. Total output tetap, sedangkan p99 TTFT meningkat. Ini merupakan ciri antrean, bukan GPU yang sibuk. Tetapkan variabel tersebut dalam systemd drop-in lalu restart. Setelah itu, periksa ollama ps karena setiap parallel slot menggandakan context yang dialokasikan dan dapat memindahkan sebagian model ke CPU.
Sebaiknya saya mengukur time to first token atau tokens per second?
Ukur keduanya karena nilainya bergerak berlawanan saat beban meningkat. TTFT adalah hal yang dirasakan pengguna, sedangkan saturated output throughput menentukan nilai pada invoice Anda. Catat p50 dan p99 TTFT pada setiap tingkat concurrency. Kemudian pilih concurrency tertinggi saat p99 TTFT masih dapat diterima. Laporkan throughput pada titik tersebut sebagai kapasitas Anda, bukan nilai maksimum di bagian atas kurva.
Apakah nilai tokens per second yang lebih tinggi selalu berarti biaya per token yang lebih rendah?
Tidak. Biaya per token adalah harga per jam dibagi jumlah token yang benar-benar dihasilkan server dalam satu jam tersebut. Jadi, server cepat yang lebih sering idle tetap memiliki biaya per token yang tinggi. Utilisasi yang menentukannya, bukan kecepatan puncak. Perhatikan juga satuannya. Total token throughput yang dicantumkan menghitung input tokens. Dengan rasio input terhadap output 1:1, nilainya hampir dua kali output rate yang ditagihkan kepada Anda.