Benchmarks¶
เฟรมเวิร์กการวัดประสิทธิภาพ (benchmark) วัดประสิทธิภาพของ inference engine ด้วยเทสต์แบบมาตรฐานที่ทำซ้ำได้ มี benchmark ในตัวสำหรับ latency และ throughput, ตัวรัน suite สำหรับการรันแบบ batch และรองรับ benchmark กำหนดเอง
ภาพรวม¶
OpenJarvis มาพร้อม benchmark สองตัว:
| Benchmark | Registry Key | วัดอะไร |
|---|---|---|
| Latency | latency |
Latency ของการอนุมานต่อการเรียก (mean, p50, p95, min, max) |
| Throughput | throughput |
Throughput เป็น token ต่อวินาที |
BaseBenchmark ABC¶
Benchmark ทั้งหมด implement abstract base class BaseBenchmark
from abc import ABC, abstractmethod
from openjarvis.bench._stubs import BenchmarkResult
from openjarvis.engine._stubs import InferenceEngine
class BaseBenchmark(ABC):
@property
@abstractmethod
def name(self) -> str:
"""Short identifier for this benchmark."""
@property
@abstractmethod
def description(self) -> str:
"""Human-readable description of what this benchmark measures."""
@abstractmethod
def run(
self,
engine: InferenceEngine,
model: str,
*,
num_samples: int = 10,
) -> BenchmarkResult:
"""Execute the benchmark and return results."""
BenchmarkResult¶
การรัน benchmark แต่ละครั้งสร้าง BenchmarkResult:
| ฟิลด์ | ชนิด | คำอธิบาย |
|---|---|---|
benchmark_name |
str |
ชื่อของ benchmark |
model |
str |
โมเดลที่ใช้ |
engine |
str |
Engine backend ที่ใช้ |
metrics |
dict[str, float] |
คู่ key-value ของ metric ที่วัดได้ |
metadata |
dict[str, Any] |
Metadata เพิ่มเติม |
samples |
int |
จำนวน sample ที่รัน |
errors |
int |
จำนวน error ที่พบ |
Benchmark ในตัว¶
Latency Benchmark¶
วัด latency ของการอนุมานต่อการเรียก ด้วย prompt สั้นๆ ที่ตายตัว แต่ละ sample ส่ง prompt ง่ายๆ ไปยัง engine แล้ววัดเวลาแบบ wall-clock
Prompt ที่ใช้: benchmark นี้หมุนเวียนใช้ prompt สำเร็จรูปสั้นๆ ("Hello", "What is 2+2?", "Explain gravity in one sentence") เพื่อให้ความหลากหลายของ input คงที่ในทุกการรัน
Metric ที่สร้าง:
| Metric | คำอธิบาย |
|---|---|
mean_latency |
Latency เฉลี่ยของ sample ที่สำเร็จทั้งหมด |
p50_latency |
Latency มัธยฐาน (percentile ที่ 50) |
p95_latency |
Latency percentile ที่ 95 (ประสิทธิภาพส่วนหาง) |
min_latency |
การเรียกที่เร็วที่สุด |
max_latency |
การเรียกที่ช้าที่สุด |
ตัวอย่างผลลัพธ์:
latency (10 samples, 0 errors)
mean_latency: 0.2345
p50_latency: 0.2100
p95_latency: 0.3800
min_latency: 0.1500
max_latency: 0.4200
Throughput Benchmark¶
วัด throughput ของการอนุมานเป็น token ต่อวินาที แต่ละ sample ส่ง prompt ที่ยาว กว่า ("Write a short paragraph about artificial intelligence") แล้ววัดทั้ง เวลาที่ใช้และจำนวน completion token ที่สร้างขึ้น
Metric ที่สร้าง:
| Metric | คำอธิบาย |
|---|---|
tokens_per_second |
Completion token ทั้งหมด / เวลาทั้งหมด |
total_tokens |
Completion token ทั้งหมดรวมทุก sample |
total_time_seconds |
เวลาแบบ wall-clock ทั้งหมดรวมทุก sample |
ตัวอย่างผลลัพธ์:
throughput (10 samples, 0 errors)
tokens_per_second: 45.6789
total_tokens: 1250.0000
total_time_seconds: 27.3600
การตีความผลลัพธ์¶
Metric ของ Latency¶
- mean_latency: เวลาตอบสนองเฉลี่ย ใช้สำหรับเปรียบเทียบประสิทธิภาพทั่วไป
- p50_latency (มัธยฐาน): เวลาตอบสนองทั่วไป ได้รับผลกระทบจาก outlier น้อยกว่าค่าเฉลี่ย
- p95_latency: เวลาตอบสนองที่แย่ที่สุดของ 95% ของคำขอ สำคัญต่อประสบการณ์ผู้ใช้ -- หากค่านี้สูงเกินไป ผู้ใช้บางส่วนจะเจอความล่าช้าที่สังเกตได้
- min/max_latency: การเรียกเดี่ยวที่ดีที่สุดและแย่ที่สุด ช่องว่างระหว่าง min กับ max ที่มากบ่งชี้ว่าประสิทธิภาพไม่สม่ำเสมอ
สิ่งที่ควรสังเกต
การตั้งค่าที่สุขภาพดีจะมี p95 / p50 < 2 หาก p95 สูงกว่ามัธยฐานมาก ให้ตรวจสอบว่า engine กำลังเจอปัญหาการแย่งชิงทรัพยากร, thermal throttling หรือความกดดันด้านหน่วยความจำหรือไม่
Metric ของ Throughput¶
- tokens_per_second: ตัวชี้วัด throughput หลัก ยิ่งสูงยิ่งดี ช่วงทั่วไป:
- เฉพาะ CPU: 5-20 token/วินาที
- GPU ระดับผู้ใช้ทั่วไป (RTX 3060-4090): 30-100 token/วินาที
- GPU ระดับ datacenter (A100, H100): 100-500+ token/วินาที
- total_tokens / total_time: ข้อมูลดิบเบื้องหลังการคำนวณ throughput มีประโยชน์สำหรับตรวจสอบว่า engine กำลังสร้างผลลัพธ์ที่มีความหมาย (ไม่ใช่การตอบกลับว่างเปล่า)
BenchmarkSuite¶
คลาส BenchmarkSuite รัน benchmark หลายตัวพร้อมกัน และมี utility สำหรับรวม
ผลลัพธ์และ serialize
from openjarvis.bench._stubs import BenchmarkSuite
from openjarvis.bench.latency import LatencyBenchmark
from openjarvis.bench.throughput import ThroughputBenchmark
suite = BenchmarkSuite([LatencyBenchmark(), ThroughputBenchmark()])
# Run all benchmarks
results = suite.run_all(engine, model, num_samples=20)
# Serialize to JSONL (one JSON object per line)
jsonl = suite.to_jsonl(results)
# Get a summary dict
summary = suite.summary(results)
Method¶
| Method | คืนค่า | คำอธิบาย |
|---|---|---|
run_all(engine, model, num_samples=10) |
list[BenchmarkResult] |
รัน benchmark ทั้งหมดตามลำดับ |
to_jsonl(results) |
str |
Serialize ผลลัพธ์เป็นรูปแบบ JSONL |
summary(results) |
dict[str, Any] |
สร้าง dictionary สรุปผล |
รูปแบบ JSONL¶
แต่ละบรรทัดในผลลัพธ์ JSONL เป็น object JSON:
{"benchmark_name": "latency", "model": "qwen3:8b", "engine": "ollama", "metrics": {"mean_latency": 0.234, "p50_latency": 0.21, "p95_latency": 0.38, "min_latency": 0.15, "max_latency": 0.42}, "metadata": {}, "samples": 10, "errors": 0}
{"benchmark_name": "throughput", "model": "qwen3:8b", "engine": "ollama", "metrics": {"tokens_per_second": 45.67, "total_tokens": 1250.0, "total_time_seconds": 27.36}, "metadata": {}, "samples": 10, "errors": 0}
รูปแบบ Summary¶
{
"benchmark_count": 2,
"benchmarks": [
{
"name": "latency",
"model": "qwen3:8b",
"engine": "ollama",
"metrics": {"mean_latency": 0.234, ...},
"samples": 10,
"errors": 0
},
{
"name": "throughput",
"model": "qwen3:8b",
"engine": "ollama",
"metrics": {"tokens_per_second": 45.67, ...},
"samples": 10,
"errors": 0
}
]
}
การใช้งานผ่าน CLI¶
# Run all benchmarks with default settings (10 samples)
jarvis bench run
# Run with more samples for better statistical accuracy
jarvis bench run -n 50
# Run only the latency benchmark
jarvis bench run -b latency
# Run only the throughput benchmark with 20 samples
jarvis bench run -b throughput -n 20
# Specify model and engine
jarvis bench run -m qwen3:8b -e ollama
# Output JSON summary to stdout
jarvis bench run --json
# Write JSONL results to a file
jarvis bench run -o results.jsonl
# Combine options
jarvis bench run -b latency -n 100 -m qwen3:8b --json -o latency.jsonl
| ตัวเลือก | ชนิด | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
-m, --model MODEL |
string | อัตโนมัติ | โมเดลที่จะ benchmark |
-e, --engine ENGINE |
string | อัตโนมัติ | Engine backend |
-n, --samples N |
int | 10 |
จำนวน sample ต่อ benchmark |
-b, --benchmark NAME |
string | ทั้งหมด | Benchmark เฉพาะที่จะรัน (latency หรือ throughput) |
-o, --output PATH |
path | ไม่มี | เขียนผลลัพธ์ JSONL ลงไฟล์ |
--json |
flag | ปิด | แสดงผลลัพธ์สรุปแบบ JSON ทาง stdout |
เพิ่ม Benchmark กำหนดเอง¶
สร้าง benchmark กำหนดเองได้ด้วยการ subclass BaseBenchmark และลงทะเบียนกับ
BenchmarkRegistry
ขั้นตอนที่ 1: Implement Benchmark¶
import time
from openjarvis.bench._stubs import BaseBenchmark, BenchmarkResult
from openjarvis.core.registry import BenchmarkRegistry
from openjarvis.core.types import Message, Role
from openjarvis.engine._stubs import InferenceEngine
class ContextLengthBenchmark(BaseBenchmark):
"""Measures how latency scales with input length."""
@property
def name(self) -> str:
return "context_length"
@property
def description(self) -> str:
return "Measures latency scaling with increasing input length"
def run(
self,
engine: InferenceEngine,
model: str,
*,
num_samples: int = 10,
) -> BenchmarkResult:
latencies = {}
errors = 0
for length in [100, 500, 1000, 2000]:
prompt = "x " * length
messages = [Message(role=Role.USER, content=prompt)]
t0 = time.time()
try:
engine.generate(messages, model=model)
latencies[f"latency_{length}_tokens"] = time.time() - t0
except Exception:
errors += 1
return BenchmarkResult(
benchmark_name=self.name,
model=model,
engine=engine.engine_id,
metrics=latencies,
samples=len(latencies),
errors=errors,
)
ขั้นตอนที่ 2: ลงทะเบียน Benchmark¶
ใช้รูปแบบ ensure_registered() เพื่อให้รอดจากการล้าง registry ในเทสต์:
def ensure_registered() -> None:
"""Register the benchmark if not already present."""
if not BenchmarkRegistry.contains("context_length"):
BenchmarkRegistry.register_value("context_length", ContextLengthBenchmark)
หรือใช้ decorator ตอนกำหนดคลาสก็ได้:
รูปแบบ ensure_registered()
ฟังก์ชัน ensure_registered() เป็นที่นิยมกว่า decorator สำหรับโมดูล benchmark เพราะรอดจากการล้าง registry ระหว่างทดสอบได้ benchmark ในตัวทั้ง latency และ throughput ใช้รูปแบบนี้ทั้งคู่ คำสั่ง CLI ของ benchmark เรียก ensure_registered() ก่อนค้นหา benchmark เสมอ
ขั้นตอนที่ 3: ใช้ Benchmark ของคุณ¶
เมื่อลงทะเบียนแล้ว benchmark ของคุณจะใช้งานได้ผ่าน CLI:
และผ่าน BenchmarkSuite: