SSD Nodes Learn Hosting plans →
คู่มือ Matt Connorโดย Matt Connor

วิธีวัดค่า Tokens per second บน Local LLM อย่างแม่นยำ

การเช่า GPU จะคุ้มค่ากว่าจ่ายราย token ก็ต่อเมื่อมีปริมาณงานสูงเกินเกณฑ์ที่กำหนด เรียนรู้วิธีวัดค่า throughput ด้วยการทำ concurrency sweep เพื่อตัดสินใจเลือกโครงสร้างพื้นฐานให้เหมาะสม

เหตุผลที่ tokens per second เป็นตัวตัดสินความคุ้มค่าของ GPU

Tokens per second คืออัตราที่เซิร์ฟเวอร์ของคุณสร้างข้อความเอาต์พุต และเป็นตัวเลขที่ใช้ตัดสินว่าการเช่า GPU นั้นถูกกว่าการจ่ายค่า API แบบราย token หรือไม่ โดย GPU จะถูกคิดค่าบริการเป็นรายชั่วโมงไม่ว่าเครื่องจะทำงานอยู่หรือว่างงาน ในขณะที่ API แบบ hosted จะคิดค่าบริการตามจำนวน token ดังนั้น GPU จะคุ้มค่าก็ต่อเมื่อคุณมีอัตราการสร้างเอาต์พุตที่สูงพอตลอดระยะเวลาส่วนใหญ่ที่คุณจ่ายเงินไป

นั่นหมายความว่าคุณจำเป็นต้องมีการวัดผลจริง ไม่ใช่เพียงตัวเลขที่อ่านมาจากที่อื่น หน้านี้จะกำหนดตัวเลข 4 ค่าที่ควรบันทึกไว้ พร้อมทั้งระบุคำสั่งที่ใช้สร้างตัวเลขเหล่านั้น และวิธีคำนวณเพื่อนำไปสู่การตัดสินใจ

เหตุใดตัวเลข tokens per second ที่เผยแพร่จึงไม่ใช่ตัวเลขของคุณ

DigitalOcean ได้เผยแพร่ตัวเลข throughput ในเดือนกรกฎาคม 2026 สำหรับ NVIDIA H200 หนึ่งตัวที่รัน llama3.3-70b-instruct ในรูปแบบ FP8 (8-bit floating point) บน vLLM ตัวเลขเหล่านี้มีประโยชน์แต่ไม่ใช่ตัวเลขของคุณ

แผนภูมิPublished DigitalOcean H200 figures, llama3.3-70b-instruct FP8, July 2026
ข้อมูลเบื้องหลังแผนภูมินี้
[
  {
    "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"
  }
]

ทุกแถวข้างต้นอ้างอิงมาจากหน้านั้น และสองแถวเป็นค่าต่ำสุดของช่วงที่ระบุไว้ ดังนั้นให้อ่านค่าทั้งสองนั้นเป็นค่าพื้นฐาน ไม่มีข้อมูลใดในตารางนี้ที่เราเป็นผู้ตรวจวัดเอง

เริ่มจากสองแถวสุดท้าย หัวข้อหลักคือ 4,071.6 tok/s ในขณะที่อัตราเฉพาะส่วน output คือ 2,036 tok/s หัวข้อหลักนับรวมทั้ง input และ output tokens เข้าด้วยกัน การทดสอบนั้นใช้ input tokens 1,024 ตัวเทียบกับ output tokens 1,024 ตัว ดังนั้นเกือบครึ่งหนึ่งของหัวข้อหลักคือ output การแยกส่วนนี้มีความสำคัญเพราะ output คือส่วนที่คุณถูกเรียกเก็บเงินและเป็นส่วนที่ช้ากว่า Prefill (การอ่าน prompt) จะประมวลผล input tokens ทั้งหมดในรอบเดียว Decode (การเขียนคำตอบ) จะสร้างทีละหนึ่ง token หัวข้อหลักของ throughput รวมจึงเป็นการเฉลี่ยตัวเลขที่ถูกกับตัวเลขที่แพงเข้าด้วยกัน

มาดูแถวแรก H200 ตัวเดียวกันที่ให้บริการทีละหนึ่งคำขอสร้างได้ 47 tok/s ดังนั้นตัวเลขเมื่อระบบทำงานเต็มประสิทธิภาพ (saturated) จึงสูงกว่าถึงสี่สิบเท่าบนฮาร์ดแวร์เดียวกัน ช่องว่างนี้เกิดขึ้นเพราะขั้นตอนการ decode เพียงขั้นตอนเดียวทำให้ GPU ต้องรอหน่วยความจำเกือบตลอดเวลา และคำขอที่เข้ามาพร้อมกันจะช่วยเติมเต็มช่วงเวลาที่ว่างงานนั้น แถวที่สอง 236 tok/s คือ H100 ตัวเดียวบนโมเดลเดียวกัน ซึ่งถูกจำกัดด้วย KV cache (key and value cache ซึ่งเป็นหน่วยความจำต่อคำขอที่การสนทนาที่กำลังให้บริการเก็บไว้บนการ์ด) การ์ดขนาด 80 GB รองรับคำขอพร้อมกันได้น้อยกว่าสำหรับโมเดลขนาด 70B ดังนั้นจึงถึงจุดอิ่มตัวที่ต่ำกว่า

หากเปลี่ยนโมเดลหรือเปลี่ยนอัตราส่วน input ต่อ output ตัวเลขทุกตัวข้างต้นจะเปลี่ยนไป ตัวเลขที่เผยแพร่เป็นการตั้งความคาดหวังของคุณ ไม่ใช่การตั้งงบประมาณของคุณ ซึ่งเป็นกฎเดียวกับที่ใช้ในการ ทำ benchmarking VPS อย่างตรงไปตรงมา ทั้งในส่วนของดิสก์และเครือข่าย

ตัวเลขสี่ประการที่สำคัญ

  • Time to first token (TTFT): ระยะเวลาระหว่างการส่งคำขอจนถึงโทเค็นแรกที่ได้รับกลับมา ซึ่งประกอบด้วยเวลาในการประมวลผลล่วงหน้า (prefill) และเวลาในคิว ผู้ใช้งานจะสัมผัสถึงค่านี้ได้โดยตรง
  • Output tokens per second (ต่อสตรีม): ความเร็วในการเขียนคำตอบหลังจากเริ่มแสดงผลแล้ว หากความเร็วสูงกว่า 20 tok/s จะถือว่าเร็วกว่าความเร็วในการอ่านของคนส่วนใหญ่ ดังนั้นการเพิ่มความเร็วในส่วนนี้จึงให้ผลตอบแทนน้อย
  • Saturated total output throughput: ผลรวมของปริมาณงานจากทุกสตรีมที่ทำงานพร้อมกันเมื่อเซิร์ฟเวอร์รับโหลดเต็มที่ นี่คือตัวเลขที่บ่งบอกถึงขีดความสามารถของระบบและเป็นตัวเลขที่สะท้อนถึงความคุ้มค่าของค่าใช้จ่ายด้าน GPU
  • p50 และ p99 TTFT ภายใต้สภาวะที่มีการใช้งานพร้อมกัน: p50 คือค่าของคำขอที่อยู่ตรงกลาง ส่วน p99 คือค่าที่คำขอ 99 จาก 100 รายการจะได้รับผลลัพธ์ภายในระยะเวลานี้ การรอคิวจะปรากฏให้เห็นในค่า p99 ก่อนเสมอ

ตัวเลขสองค่าแรกจะดีขึ้นเมื่อเซิร์ฟเวอร์มีภาระงานน้อย ส่วนค่าที่สามจะดีขึ้นเมื่อเซิร์ฟเวอร์มีภาระงานสูง ตัวเลขเหล่านี้มีผลกระทบที่ขัดแย้งกัน จึงไม่มีตัวเลขเพียงค่าเดียวที่สามารถอธิบายประสิทธิภาพของเครื่องเซิร์ฟเวอร์ได้ทั้งหมด

แก้ไขความยาวของอินพุตและเอาต์พุตก่อนทำการวัดผล

ปริมาณงาน (throughput) ขึ้นอยู่กับรูปแบบของทราฟฟิก พรอมต์ขนาด 4,000 token ที่มีคำตอบ 50 token ถือเป็นงานที่เน้นการ prefill ในขณะที่พรอมต์ขนาด 200 token ที่มีคำตอบ 2,000 token ถือเป็นงานที่เน้นการ decode เซิร์ฟเวอร์เดียวกันจะรายงานค่า tokens per second ที่แตกต่างกันอย่างมากสำหรับทั้งสองกรณีนี้ ดังนั้นให้เลือกอัตราส่วนมาหนึ่งค่า เขียนกำกับไว้ข้างตัวเลขทุกตัวที่คุณบันทึก และห้ามนำค่าที่มาจากอัตราส่วนต่างกันมาเปรียบเทียบกัน ค่าอินพุต 1,024 และเอาต์พุต 1,024 เป็นค่าเริ่มต้นที่เหมาะสมเนื่องจากผู้ให้บริการหลายรายเผยแพร่ผลการทดสอบที่อัตราส่วนนี้ หากคุณทราบลักษณะทราฟฟิกจริงของคุณ ให้ใช้ทราฟฟิกจริงนั้น

บังคับความยาวของเอาต์พุตด้วย โมเดลที่หยุดทำงานหลังจากสร้างไป 60 token จะทำให้การรันสั้นลงและดูเหมือนว่าทำงานได้เร็วขึ้น เนื่องจากค่า TTFT จะกลายเป็นสัดส่วนที่ใหญ่ขึ้นของเวลาทั้งหมด แฟล็ก --ignore-eos ใน vLLM benchmark client จะทำให้ทุกคำขอสร้างจำนวน token ตามที่ระบุไว้พอดี เพื่อให้การรันสองครั้งสามารถเปรียบเทียบกันได้ การเลือกโมเดลส่งผลต่อตัวเลขเหล่านี้มากกว่าการใช้แฟล็กใดๆ โดยเนื้อหา การติดตั้งโมเดล Qwen 3 ลงบน GPU ของ VPS เครื่องเดียว จะครอบคลุมถึงประเด็นด้านหน่วยความจำสำหรับการเลือกดังกล่าว

วัดค่าจากสตรีมเดียวเป็นอันดับแรก

เริ่มต้นด้วยกรณีที่ง่ายที่สุด นี่คือการตรวจสอบความถูกต้องเบื้องต้นและเป็นค่าขีดจำกัดสูงสุด Ollama จะแสดงเวลาการทำงานของตัวเองออกมา

ollama run llama3.1:8b --verbose "Write 200 words about disk scheduling."

บรรทัดที่ต้องอ่านคือ eval rate ซึ่งเป็นจำนวนโทเค็นที่สร้างได้ต่อวินาที prompt eval rate คืออัตราการประมวลผลช่วงแรก (prefill rate) และ load duration คือเวลาที่ใช้ในการโหลดโมเดลเข้าสู่ VRAM ในการเรียกใช้งานครั้งแรกหลังจากเริ่มระบบใหม่ load duration จะมีค่าสูง ทำให้ค่า total duration อาจทำให้เข้าใจผิดได้ ให้รันคำสั่งสองครั้งแล้วอ่านผลลัพธ์จากครั้งที่สอง โดยปกติ Ollama จะยกเลิกการโหลดโมเดลที่ไม่ได้ใช้งานหลังจากผ่านไปห้านาที ดังนั้นการเว้นช่วงนานเกินไประหว่างการรันจะทำให้คุณกลับไปอยู่ในสถานะเริ่มระบบใหม่ (cold case) อีกครั้ง

ฟิลด์เดียวกันนี้สามารถดึงมาจาก API ซึ่งเขียนสคริปต์จัดการได้ง่ายกว่า

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 มีหน่วยเป็นนาโนวินาที ดังนั้นการหารด้วย 1,000,000,000 จะได้ค่าเป็นวินาที การหารดังกล่าวเป็นวิธีที่เอกสาร API ของ Ollama กำหนดไว้สำหรับการคำนวณโทเค็นต่อวินาที หากเซิร์ฟเวอร์ยังไม่พร้อมใช้งาน การโฮสต์ LLM ด้วยตนเองโดยใช้ Ollama บน VPS จะครอบคลุมขั้นตอนการติดตั้งและหน่วย systemd

ค่า TTFT จำเป็นต้องใช้การร้องขอแบบสตรีมมิ่ง ซึ่ง curl สามารถจับเวลาส่วนนี้ให้คุณได้

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 คือช่วงเวลาที่ไบต์แรกของเนื้อหาการตอบกลับมาถึง ในการทำ chat completion แบบสตรีมมิ่ง ไบต์นั้นจะเป็นส่วนหนึ่งของ server-sent event แรก ซึ่งอาจเป็นโทเค็นเนื้อหาตัวแรกหรือส่วนต่าง (delta) ของบทบาทที่ส่งมาก่อนหน้า ดังนั้นให้ถือว่าค่านี้คือ TTFT โดยบวกลบหนึ่งอีเวนต์ ซึ่งมีความแม่นยำเพียงพอสำหรับการเปรียบเทียบการรันสองครั้งบนเซิร์ฟเวอร์เดียวกัน

ตัวเลขจากสตรีมเดียวนั้นแสดงประสิทธิภาพของเครื่องในแง่บวกเกินจริง ค่า TTFT จะเป็นค่าที่ดีที่สุดเท่าที่จะเป็นไปได้เพราะไม่มีคิวงานอื่นรออยู่ข้างหน้า และอัตราต่อสตรีมก็จะเป็นค่าที่ดีที่สุดเพราะการ์ดจอทั้งใบกำลังประมวลผลเพียงคำขอเดียว ทั้งสองค่านี้ไม่ได้บ่งบอกถึงขีดความสามารถในการรองรับงานจริงของเครื่อง

คุณจะทำการทดสอบ Concurrency Sweep ได้อย่างไร

การทดสอบแบบ Sweep คือการรัน Workload ชุดหนึ่งที่ระดับ Concurrency เพิ่มขึ้นเรื่อยๆ เพื่อบันทึกผลลัพธ์ที่เกิดขึ้นในแต่ละขั้นตอน vLLM มีไคลเอนต์สำหรับงานนี้มาให้ในตัว และเนื่องจากไคลเอนต์ดังกล่าวรองรับ OpenAI API จึงสามารถใช้งานร่วมกับ Ollama หรือบริการอื่นที่รองรับมาตรฐาน 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 ใช้สำหรับจำกัดจำนวนคำขอที่กำลังประมวลผลอยู่ (requests in flight) ซึ่งเป็นตัวแปรที่คุณต้องปรับเปลี่ยนในการทำ Sweep ส่วน --num-prompts คือจำนวนคำขอทั้งหมดที่ส่งไป ดังนั้นควรตั้งค่าให้มีค่าประมาณ 10 เท่าของ Concurrency เพื่อให้ได้ค่าเฉลี่ยที่เสถียร ผลลัพธ์สรุปจะแสดง Output token throughput (tok/s): และ Total token throughput (tok/s): ตามด้วย Mean TTFT (ms):, Median TTFT (ms): และ P99 TTFT (ms): ภายใต้หัวข้อ Time to First Token

แม้ในผลลัพธ์จะไม่มีอัตราต่อสตรีมแสดงไว้โดยตรง แต่สามารถคำนวณได้ง่ายๆ Mean TPOT (ms): คือเวลาเฉลี่ยต่อหนึ่ง Output Token หลังจาก Token แรก ดังนั้นหากได้ค่า 25 ms ต่อ Token จะเท่ากับ 40 Token ต่อวินาทีต่อสตรีม การนำ Output Throughput หารด้วย Concurrency ก็จะได้ผลลัพธ์เดียวกัน

จากนั้นให้ทำซ้ำเป็นลูปพร้อมบันทึกผลการรันแต่ละครั้ง

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"
done
การอ่านไฟล์ JSON ที่บันทึกไว้

เนื่องจากการรันแต่ละครั้งจะเขียนไฟล์แยกกันหนึ่งไฟล์ คุณสามารถดึงเฉพาะฟิลด์ที่ต้องการจากทุกไฟล์พร้อมกันได้

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"
done

output_throughput คือจำนวน Output Tokens ต่อวินาที ส่วน total_token_throughput จะรวม Input Tokens เข้าไปด้วย ดังนั้นที่อัตราส่วน 1:1 ค่านี้จะใกล้เคียงกับสองเท่าของค่าแรก ส่วน p99_ttft_ms จะปรากฏขึ้นก็ต่อเมื่อ --metric-percentiles มีค่ารวมถึง 99 หากคุณระบุ Percentile ที่ไม่ได้ร้องขอไว้ jq จะแสดงค่า null ออกมา

การกวาดหาค่าความพร้อมกัน (concurrency sweep) แสดงให้เห็นอะไรบ้าง

แผนภูมิIllustrative sweep shape: 8B model, one 24 GB GPU, 1024 in / 1024 out
ข้อมูลเบื้องหลังแผนภูมินี้
[
  {
    "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 แถวเหล่านั้นเป็นตัวอย่างของรูปแบบที่ได้จากการกวาดหาค่าบนเครื่อง GPU ขนาดเล็กที่เช่าใช้งาน โดยแสดงในระดับขนาดที่สมเหตุสมผล ข้อมูลนี้ไม่ใช่การวัดผลจากเซิร์ฟเวอร์ของคุณและไม่ใช่ตัวเลขจากผู้จำหน่าย ให้คุณรันลูปข้างต้นและแทนที่ด้วยข้อมูลของคุณเอง

จงอ่านรูปแบบของข้อมูล เพราะรูปแบบคือสิ่งที่นำไปประยุกต์ใช้ทั่วไปได้ ที่ระดับ 1 สตรีม เครื่องทั้งเครื่องผลิตโทเค็นได้ 92 โทเค็นต่อวินาที โดยมีค่า p99 TTFT อยู่ที่ 61 มิลลิวินาที เมื่อถึงระดับ 128 streams ปริมาณงานรวมจะเพิ่มขึ้นเป็น 2304 โทเค็นต่อวินาที ซึ่งสูงขึ้น 25 เท่า ในขณะที่แต่ละสตรีมจะลดลงเหลือ 18 โทเค็นต่อวินาที และค่า p99 TTFT จะเพิ่มขึ้นเป็น 3820 มิลลิวินาที ปริมาณงานรวมที่เพิ่มขึ้นเกิดจากการทำ batching ซึ่งเปลี่ยนช่วงเวลาที่หน่วยความจำว่างงานให้กลายเป็นงานที่มีประโยชน์ ส่วนความเร็วต่อสตรีมที่ลดลงเป็นเพราะทรัพยากรประมวลผลถูกแบ่งใช้งานร่วมกัน

การเพิ่มจำนวนขึ้นเป็นสองเท่าในครั้งสุดท้ายคือจุดสังเกตสำคัญ การเพิ่มจาก 64 เป็น 128 สตรีมทำให้ปริมาณงานรวมเพิ่มขึ้นไม่ถึงร้อยละ 6 ในขณะที่ค่า p99 TTFT เพิ่มขึ้นประมาณ 3 เท่า ซึ่งหมายความว่า KV cache เต็มแล้วและคำขอต่างๆ กำลังเข้าคิวแทนที่จะได้รับการประมวลผล จุดปฏิบัติงานที่เหมาะสมจะอยู่ก่อนหน้านั้น คือที่ 32 สตรีม เครื่องยังคงผลิตได้ 1728 โทเค็นต่อวินาที ซึ่งคิดเป็นร้อยละ 75 ของจุดสูงสุด โดยได้ความเร็ว 54 โทเค็นต่อวินาทีต่อสตรีม และมีค่า p99 TTFT อยู่ที่ 498 มิลลิวินาที ให้รายงานจุดนั้นเป็นขีดความสามารถของคุณ จุดสูงสุดของกราฟคือตัวเลขที่คุณไม่สามารถนำไปใช้ให้บริการผู้ใช้งานจริงได้

Ollama และ vLLM ไม่ได้วัดค่าสิ่งเดียวกัน

หากรันการทดสอบ sweep กับเซิร์ฟเวอร์ Ollama ค่าเริ่มต้น ผลรวมที่ได้แทบจะไม่เปลี่ยนแปลง OLLAMA_NUM_PARALLEL มีค่าเริ่มต้นเป็น 1 ทำให้มีการประมวลผลเพียงหนึ่งคำขอในขณะที่คำขอที่เหลือต้องรอคิว ซึ่งคิวนี้เองที่ทำให้ค่า p99 TTFT เพิ่มสูงขึ้นในขณะที่ผลลัพธ์รวมยังคงที่ คุณควรปรับค่านี้ให้สูงขึ้นก่อนเริ่มการวัดผลใดๆ

sudo systemctl edit ollama.service
[Service]
Environment="OLLAMA_NUM_PARALLEL=8"

รีสตาร์ทด้วย sudo systemctl restart ollama จากนั้นตรวจสอบว่าโมเดลยังคงทำงานได้ตามปกติ แต่ละ parallel slot จะได้รับส่วนแบ่งของ context window ของตัวเอง ดังนั้นเอกสารของ Ollama จึงระบุว่า context ขนาด 2K ที่มี 4 parallel requests จะใช้พื้นที่รวม 8K หากเพิ่มจำนวน slot มากเกินไป โมเดลจะใช้ VRAM จนเต็ม ให้ตรวจสอบ ollama ps หากคอลัมน์ PROCESSOR แสดงค่าประมาณ 48%/52% CPU/GPU หมายความว่าโมเดลบางส่วนถูกย้ายไปประมวลผลบน CPU ซึ่งจะทำให้ throughput ลดลงเมื่อคุณเพิ่ม concurrency แทนที่จะเพิ่มขึ้น เมื่อเกินจำนวน parallel slots คำขอจะถูกนำไปเข้าคิวที่ OLLAMA_MAX_QUEUE ซึ่งมีค่าเริ่มต้นที่ 512 หลังจากนั้นเซิร์ฟเวอร์จะตอบกลับด้วยสถานะ 503

vLLM ใช้กลไก continuous batching จึงสามารถรับคำขอใหม่เข้าสู่ batch ที่กำลังทำงานอยู่ได้ทันทีเมื่อมี slot ว่าง ทำให้กราฟประสิทธิภาพเพิ่มสูงขึ้นเรื่อยๆ จนกว่า KV cache จะเต็ม Ollama ถูกปรับแต่งมาเพื่อเน้นการใช้งานหนึ่งโมเดลต่อหนึ่งเครื่องและมีค่าใช้จ่ายในการตั้งค่าต่ำ ดังนั้นเอนจินทั้งสองจึงให้ผลลัพธ์ที่แตกต่างกันในการทดสอบ sweep เดียวกัน ซึ่งเป็นหัวข้อหลักของ การเปรียบเทียบ Ollama และ vLLM ในฐานะเอนจินสำหรับให้บริการ โปรดบันทึกข้อมูลว่าเอนจินใดและเวอร์ชันใดเป็นผู้สร้างตัวเลขแต่ละชุดขึ้นมา

ห้าวิธีในการวัดผลที่ผิดพลาด

  • ไคลเอนต์อยู่ไกลเกินไป การทำ Benchmarking จากแล็ปท็อปของคุณผ่านอินเทอร์เน็ตจะเพิ่มเวลา Round trip เข้าไปในทุกค่า TTFT ทำให้คุณกำลังวัดประสิทธิภาพการเชื่อมต่อที่บ้านของคุณเอง ให้รันไคลเอนต์ในภูมิภาคเดียวกับเซิร์ฟเวอร์
  • โมเดลยังไม่พร้อมทำงาน (Cold) คำขอแรกต้องเสียเวลาโหลดน้ำหนักโมเดล และใน vLLM อาจต้องเสียเวลาทำ graph capture ด้วย ให้ส่งชุดข้อมูลสำหรับวอร์มอัพและทิ้งผลลัพธ์นั้นไป
  • Prefix caching ช่วยตอบแทนคุณ vLLM เปิดใช้งาน automatic prefix caching เป็นค่าเริ่มต้น ดังนั้นการส่ง prompt เดิมซ้ำๆ จะเป็นการวัดประสิทธิภาพของแคชแทนที่จะเป็น prefill และทำให้ค่า TTFT ต่ำกว่าความเป็นจริงอย่างมาก --dataset-name random ช่วยหลีกเลี่ยงปัญหานี้ได้เนื่องจากทุก prompt มีความแตกต่างกัน เพื่อความมั่นใจ ให้เริ่มเซิร์ฟเวอร์ด้วย --no-enable-prefix-caching
  • ผลลัพธ์สั้นเกินไป สำหรับคำตอบขนาด 32-token ค่า TTFT จะส่งผลต่อแต่ละคำขอมากเกินไป และค่า tokens per second ที่ได้จะเป็นเพียงการวัด prefill เท่านั้น ให้ใช้ --ignore-eos พร้อมกับความยาวผลลัพธ์ที่สมจริง
  • คุณรายงานค่า concurrency เป็น 1 นี่เป็นตัวเลขที่ดูดีที่สุดในรายงาน แต่ไม่มีผลต่อการคำนวณต้นทุนจริง

เปลี่ยนตัวเลขที่วัดได้ให้เป็นการตัดสินใจ

ให้นำค่า throughput ขาออกสูงสุดที่วัดได้จากการทำ sweep (ไม่ใช่ความเร็วของ single-stream) มาเปรียบเทียบกับราคาต่อ token จุดคุ้มทุนคำนวณได้จากการหารกันง่ายๆ ดังนี้:

break_even_tok_s = (price_per_hour / price_per_million_output_tokens) * 1000000 / 3600

ลองคำนวณโดยใช้ราคาของ DigitalOcean ในเดือนกรกฎาคม 2026 โดย inference endpoint แบบ dedicated รุ่น H200 มีราคาอยู่ที่ 4.47 ดอลลาร์ต่อชั่วโมง และแบบ serverless อยู่ที่ 0.65 ดอลลาร์ต่อล้าน token ดังนั้น 4.47 หารด้วย 0.65 จะได้ 6.88 ล้าน token ต่อชั่วโมง และเมื่อหารด้วย 3,600 วินาที จะได้ประมาณ 1,910 output tokens ต่อวินาที ราคาเหล่านี้เป็นราคาของทางผู้ให้บริการ ส่วนการหารเป็นวิธีของเรา เมื่อเราตรวจสอบในวันที่ 7 ตุลาคม 2026 พบว่า H200 endpoint ยังคงราคา 4.47 ดอลลาร์ต่อชั่วโมง แต่ DigitalOcean ได้ยกเลิกบริการ serverless Llama 3.3 70B ไปเมื่อวันที่ 18 สิงหาคม 2026 และแนะนำให้ผู้ใช้ไปใช้ Llama 4 Maverick แทน ซึ่งมีราคาอยู่ที่ 0.87 ดอลลาร์ต่อล้าน output tokens เนื่องจากเป็นโมเดลที่ต่างกัน จึงจำเป็นต้องทำ sweep ใหม่ก่อนที่การเปรียบเทียบจะมีนัยสำคัญ

คำสำคัญที่ใช้ตัดสินใจเรื่องนี้คือ ความต่อเนื่อง (sustained) การทำความเร็วได้ 1,910 tokens ต่อวินาทีที่จุดอิ่มตัวเป็นเวลาสองชั่วโมงต่อวัน ไม่ถือว่าเป็น 1,910 tokens ต่อวินาทีแบบต่อเนื่อง เพราะคุณต้องจ่ายเงินสำหรับอีกยี่สิบสองชั่วโมงที่เหลือด้วย การเช่าการ์ดจอในรูปแบบ GPU Droplet ไม่ได้ช่วยลดเกณฑ์นี้ลง: ณ วันที่ 7 ตุลาคม 2026 ราคา Droplet ของ DigitalOcean ระบุว่า H200 ราคา 4.47 ดอลลาร์ต่อชั่วโมงแบบ on-demand ซึ่งเท่ากับราคา endpoint ส่วน NVIDIA Droplet รุ่นที่ถูกที่สุดอย่าง RTX 4000 ราคา 0.76 ดอลลาร์ต่อชั่วโมง และ L40S หรือ RTX 6000 ราคา 1.57 ดอลลาร์ต่อชั่วโมงนั้น มีขนาดเล็กเกินกว่าจะรันโมเดล 70B ในรูปแบบ FP8 ได้ ดังนั้นจากตัวเลขข้างต้น H200 ที่ทำความเร็วสูงสุดได้ 2,036 output tokens ต่อวินาที จะต้องทำความเร็วเฉลี่ยให้ได้ประมาณ 94 เปอร์เซ็นต์ของค่าสูงสุดนั้นตลอดทุกชั่วโมงที่คุณจ่ายเงิน เพื่อให้คุ้มกับราคา 0.65 ดอลลาร์ต่อล้าน token หากต่ำกว่านั้น ราคาแบบต่อ token จะคุ้มค่ากว่า ชั่วโมงที่ GPU ว่างงาน (idle) ไม่ใช่ความเร็วของ token ที่ช้า คือสิ่งที่มักทำให้การ self-host ไม่คุ้มทุน

ดังนั้น การตัดสินใจของคุณจึงมีปัจจัยนำเข้าสองอย่าง ผลจากการทำ sweep จะให้ค่าเพดานสูงสุดแก่คุณ ส่วนรูปแบบ traffic ของคุณจะเป็นตัวกำหนดสัดส่วนของเพดานนั้นที่คุณใช้งานจริง ให้นำตัวเลขทั้งสองมาคูณกัน แล้วนำผลลัพธ์ไปดูที่ จุดคุ้มทุนระหว่าง GPU VPS กับ API แบบต่อ token เพื่ออ่านคำตอบสำหรับปริมาณการใช้งานของคุณ

FAQ

LLM ที่โฮสต์เองควรมีค่า tokens per second เท่าใดจึงจะถือว่าดี?

คำตอบมีสองแง่มุมเนื่องจากตัวชี้วัดนี้ทำหน้าที่สองอย่าง สำหรับผู้ใช้งานที่อ่านผลลัพธ์ ค่าที่สูงกว่าประมาณ 20 tokens ต่อวินาทีต่อหนึ่งสตรีมถือว่าเร็วกว่าความเร็วในการอ่านของมนุษย์แล้ว ดังนั้นการเพิ่มความเร็วให้สูงกว่านี้จึงไม่มีประโยชน์ สำหรับในแง่ของต้นทุน ตัวเลขที่สำคัญคือปริมาณ throughput รวมที่อิ่มตัว (saturated total output throughput) ซึ่งค่าที่ถือว่าดีคือค่าที่ทำให้คุ้มทุน เมื่อเทียบกับราคา 0.65 ดอลลาร์ต่อล้านโทเค็นบนเซิร์ฟเวอร์ที่มีค่าใช้จ่าย 4.47 ดอลลาร์ต่อชั่วโมง ค่าขั้นต่ำจะอยู่ที่ประมาณ 1,910 output tokens ต่อวินาทีอย่างต่อเนื่อง ณ ราคาเดือนกรกฎาคม 2026 การรันเพียงหนึ่งสตรีมบนโมเดลขนาดใหญ่ไม่มีทางถึงระดับนี้ได้ ซึ่งเป็นเหตุผลว่าทำไมจึงต้องมีการทำ batching

ทำไม throughput ของ Ollama ถึงไม่เพิ่มขึ้นเมื่อเพิ่มจำนวน concurrent requests?

OLLAMA_NUM_PARALLEL มีค่าเริ่มต้นเป็น 1 ทำให้เซิร์ฟเวอร์ประมวลผลทีละคำขอต่อหนึ่งโมเดลและนำคำขอที่เหลือเข้าคิวไว้สูงสุดถึง OLLAMA_MAX_QUEUE (ค่าเริ่มต้นคือ 512) ก่อนที่จะส่งคืนสถานะ 503 ปริมาณ output รวมจึงคงที่ในขณะที่ค่า p99 TTFT เพิ่มสูงขึ้น ซึ่งเป็นลักษณะเฉพาะของการรอคิวมากกว่าการที่ GPU ทำงานหนักเกินไป ให้กำหนดตัวแปรนี้ใน systemd drop-in แล้วรีสตาร์ท จากนั้นตรวจสอบ ollama ps เนื่องจากแต่ละ parallel slot จะเพิ่มการใช้ context และอาจผลักภาระส่วนหนึ่งของโมเดลไปที่ CPU ได้

ควรวัดค่า time to first token หรือ tokens per second?

ควรวัดทั้งสองค่าเนื่องจากทั้งคู่มีทิศทางที่สวนทางกันเมื่อโหลดเพิ่มขึ้น TTFT คือสิ่งที่ผู้ใช้สัมผัสได้ ส่วน saturated output throughput คือสิ่งที่สะท้อนในใบแจ้งหนี้ของคุณ ให้บันทึกค่า p50 และ p99 TTFT ในทุกระดับของ concurrency จากนั้นเลือกค่า concurrency สูงสุดที่ค่า p99 TTFT ยังอยู่ในระดับที่คุณยอมรับได้ แล้วรายงานค่า throughput ณ จุดนั้นว่าเป็นความจุของระบบ ไม่ใช่ค่าสูงสุดที่วัดได้จากจุดสูงสุดของกราฟ

ค่า tokens per second ที่สูงกว่าหมายถึงต้นทุนต่อโทเค็นที่ต่ำกว่าเสมอไปหรือไม่?

ไม่เสมอไป ต้นทุนต่อโทเค็นคือราคาต่อชั่วโมงหารด้วยจำนวนโทเค็นที่เซิร์ฟเวอร์ผลิตได้จริงในชั่วโมงนั้น ดังนั้นเซิร์ฟเวอร์ที่ทำงานเร็วแต่ปล่อยให้ว่างงานเกือบทั้งวันก็ยังคงมีต้นทุนต่อโทเค็นที่สูง อัตราการใช้งาน (utilisation) เป็นตัวตัดสิน ไม่ใช่ความเร็วสูงสุด นอกจากนี้ควรระวังเรื่องหน่วยวัดด้วย: ค่า total token throughput ที่ระบุไว้มักนับรวม input tokens ดังนั้นที่อัตราส่วน input ต่อ output เป็น 1:1 ค่านี้จะใกล้เคียงกับสองเท่าของอัตรา output ที่คุณถูกเรียกเก็บเงินจริง

#benchmarking#tokens-per-second#vllm#ollama#gpu-vps