accounts/docs/bench_ollama.sh
Haitao Pan 84cb83933d Add benchmarking scripts and configs:
- bench_embedding.sh / bench_ollama.sh for Ollama & embedding API tests
- hf_embedding_bench.py for HF model performance
- models.txt / models-emb.txt for test configs
- docs in bench_embedding.md / bench_ollama.md
2025-08-13 13:12:34 +08:00

178 lines
6.3 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# docs/bench_ollama.sh
# 比较多模型的 TTFT / Total / QPS / P90 / P95 / 输入输出 Tokens / 吞吐
# 支持 --input_config 模型清单;--gen-only 运行双场景(短答=交互延迟 + 长答=生成吞吐)
set -euo pipefail
# ===== Default Config (可通过环境变量覆盖) =====
ENDPOINT="${ENDPOINT:-http://127.0.0.1:11434}"
PATH_CHAT="/v1/chat/completions"
PROMPT="${PROMPT:-你好,简要介绍一下自己}"
N="${N:-20}" # hey 总请求数
C="${C:-2}" # hey 并发数
TEMP="${TEMP:-0.7}"
TIMEOUT="${TIMEOUT:-120}" # 单请求超时秒
CSV_OUTPUT="${CSV_OUTPUT:-}" # 若设为非空,输出 CSV
SHORT_TOKENS="${SHORT_TOKENS:-64}" # 交互延迟场景 max_tokens
LONG_TOKENS="${LONG_TOKENS:-200}" # 生成吞吐场景 max_tokens
# ===== 默认模型列表(可用 --input_config 覆盖)=====
MODELS=(
"qwen2.5:7b"
"qwen2.5-coder:14b"
"mistral-nemo:12b"
"llama2:7b"
"llama2:13b"
)
# ===== 解析参数 =====
RUN_DUAL="" # 空:单场景;非空:双场景
if [[ "${1:-}" == "--input_config" ]]; then
if [[ -f "${2:-}" ]]; then
mapfile -t MODELS < <(sed -e 's/#.*$//' -e '/^[[:space:]]*$/d' "$2")
shift 2
else
echo "❌ --input_config file not found: ${2:-<missing>}" && exit 1
fi
fi
if [[ "${1:-}" == "--gen-only" ]]; then
RUN_DUAL=1
shift 1
fi
# ===== 依赖检查 =====
need() { command -v "$1" >/dev/null 2>&1 || { echo "❌ 需要 $1请安装brew install $1"; exit 1; }; }
need curl; need hey; need jq; need awk; need sed
# ===== 工具函数 =====
json_payload() {
local model="$1" max_toks="$2"
cat <<EOF
{
"model": "${model}",
"messages": [{"role":"user","content":"${PROMPT}"}],
"max_tokens": ${max_toks},
"temperature": ${TEMP}
}
EOF
}
measure_curl_timing() {
local model="$1" max_toks="$2"
local delim="__CURL_TIMING__"
local resp timing http_code ttft total body prompt_tokens completion_tokens
resp=$(curl -sS -m "${TIMEOUT}" \
-H "Content-Type: application/json" \
-d "$(json_payload "$model" "$max_toks")" \
-w "\n${delim} %{http_code} %{time_starttransfer} %{time_total}" \
"${ENDPOINT}${PATH_CHAT}" || true)
timing=$(printf '%s\n' "$resp" | sed -n '$p')
read -r _ http_code ttft total <<<"$timing"
body=$(printf '%s\n' "$resp" | sed '$d')
prompt_tokens=$(printf '%s' "$body" | jq -r '.usage.prompt_tokens // 0' 2>/dev/null || echo 0)
completion_tokens=$(printf '%s' "$body" | jq -r '.usage.completion_tokens // 0' 2>/dev/null || echo 0)
echo "${http_code:-0} ${ttft:-0} ${total:-0} ${prompt_tokens:-0} ${completion_tokens:-0}"
}
run_hey() {
local model="$1" max_toks="$2" tmp
tmp="$(mktemp)"
hey -n "${N}" -c "${C}" -m POST \
-H "Content-Type: application/json" \
-D <(json_payload "$model" "$max_toks") \
"${ENDPOINT}${PATH_CHAT}" > "$tmp" 2>/dev/null || true
local reqs avg p90 p95
reqs=$(grep -E 'Requests/sec' "$tmp" | awk '{print $2}')
avg=$(grep -E '^ Average:' "$tmp" | awk '{print $2}')
p90=$(grep -E ' 90% in ' "$tmp" | awk '{print $3}')
p95=$(grep -E ' 95% in ' "$tmp" | awk '{print $3}')
rm -f "$tmp"
echo "${reqs:-NA} ${avg:-NA} ${p90:-NA} ${p95:-NA}"
}
line() { printf '%s\n' "-----------------------------------------------------------------------------------------------------"; }
print_header() {
local label="$1" mtoks="$2"
echo "🔧 Endpoint: ${ENDPOINT}${PATH_CHAT}"
echo "🗣️ Prompt: ${PROMPT}"
echo "📦 N=${N}, C=${C}, temp=${TEMP}, timeout=${TIMEOUT}s"
echo "🧪 场景:${label}max_tokens=${mtoks}"
line
printf "%-22s | %-5s | %-8s | %-8s | %-6s | %-6s | %-10s | %-10s | %-8s | %-8s | %-8s | %-9s\n" \
"Model" "HTTP" "TTFT" "Total" "InTok" "OutTok" "Req/s" "Avg(s)" "P90(s)" "P95(s)" "OutTok/s" "GenTok/s"
line
}
maybe_csv_header() {
local csv="$1"
[[ -z "$CSV_OUTPUT" ]] && return 0
echo "model,http,ttft,total,in_tok,out_tok,req_s,avg_s,p90_s,p95_s,outtok_per_s,gen_tok_per_s" > "$csv"
}
run_suite() {
local label="$1" mtoks="$2" csv="$3"
print_header "$label" "$mtoks"
maybe_csv_header "$csv"
for m in "${MODELS[@]}"; do
# 预热
curl -sS -m "${TIMEOUT}" -o /dev/null -H "Content-Type: application/json" \
-d "$(json_payload "$m" "$mtoks")" "${ENDPOINT}${PATH_CHAT}" >/dev/null 2>&1 || true
# 单次测时 + tokens
read -r http_code ttft total in_tok out_tok <<<"$(measure_curl_timing "$m" "$mtoks")"
if [[ "$http_code" != "200" ]]; then
printf "%-22s | %-5s | %-8s | %-8s | %-6s | %-6s | %-10s | %-10s | %-8s | %-8s | %-8s | %-9s\n" \
"$m" "$http_code" "FAIL" "FAIL" "-" "-" "FAIL" "FAIL" "FAIL" "FAIL" "FAIL" "FAIL"
[[ -n "$CSV_OUTPUT" ]] && echo "$m,$http_code,FAIL,FAIL,-,-,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL" >> "$csv"
continue
fi
# 并发压测
read -r reqs avg p90 p95 <<<"$(run_hey "$m" "$mtoks")"
# 吞吐(包含首字延迟)
outtok_per_s="NA"
if awk "BEGIN{exit !($total>0 && $out_tok>0)}"; then
outtok_per_s=$(awk "BEGIN {printf \"%.2f\", ${out_tok}/${total}}")
fi
# 纯生成吞吐OutTok / (Total - TTFT)
gen_tok_per_s="NA"
gen_denom=$(awk "BEGIN {d = ${total}-${ttft}; if (d>0) printf \"%.6f\", d; else printf \"0\"}")
if awk "BEGIN{exit !($gen_denom>0 && $out_tok>0)}"; then
gen_tok_per_s=$(awk "BEGIN {printf \"%.2f\", ${out_tok}/${gen_denom}}")
fi
printf "%-22s | %-5s | %-8.3f | %-8.3f | %-6s | %-6s | %-10s | %-10s | %-8s | %-8s | %-8s | %-9s\n" \
"$m" "$http_code" "$ttft" "$total" "$in_tok" "$out_tok" "$reqs" "$avg" "$p90" "$p95" "$outtok_per_s" "$gen_tok_per_s"
[[ -n "$CSV_OUTPUT" ]] && echo "$m,$http_code,$ttft,$total,$in_tok,$out_tok,$reqs,$avg,$p90,$p95,$outtok_per_s,$gen_tok_per_s" >> "$csv"
done
line
echo "说明InTok = 输入 tokensOutTok = 输出 tokensOutTok/s = OutTok/TotalGenTok/s = OutTok/(Total-TTFT)"
[[ -n "$CSV_OUTPUT" ]] && echo "• 已输出 CSV$csv"
echo
}
# ===== 执行 =====
if [[ -n "$RUN_DUAL" ]]; then
# 场景 1交互延迟短答
run_suite "交互延迟(短答)" "$SHORT_TOKENS" "bench_ollama_interactive.csv"
# 场景 2生成吞吐长答
run_suite "生成吞吐(长答)" "$LONG_TOKENS" "bench_ollama_throughput.csv"
else
# 单场景:使用 LONG_TOKENS与旧版保持一致
run_suite "单场景(默认)" "$LONG_TOKENS" "bench_ollama.csv"
fi